diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json new file mode 100644 index 000000000..4b879a682 --- /dev/null +++ b/Godeps/Godeps.json @@ -0,0 +1,93 @@ +{ + "ImportPath": "github.com/syncthing/discosrv", + "GoVersion": "go1.5.1", + "Deps": [ + { + "ImportPath": "github.com/bkaradzic/go-lz4", + "Rev": "74ddf82598bc4745b965729e9c6a463bedd33049" + }, + { + "ImportPath": "github.com/calmh/logger", + "Rev": "c96f6a1a8c7b6bf2f4860c667867d90174799eb2" + }, + { + "ImportPath": "github.com/calmh/luhn", + "Rev": "0c8388ff95fa92d4094011e5a04fc99dea3d1632" + }, + { + "ImportPath": "github.com/calmh/xdr", + "Rev": "5f7208e86762911861c94f1849eddbfc0a60cbf0" + }, + { + "ImportPath": "github.com/camlistore/lock", + "Rev": "ae27720f340952636b826119b58130b9c1a847a0" + }, + { + "ImportPath": "github.com/cznic/b", + "Rev": "e2e747ce049fb910cff6b1fd7ad8faf3900939d5" + }, + { + "ImportPath": "github.com/cznic/bufs", + "Rev": "3dcccbd7064a1689f9c093a988ea11ac00e21f51" + }, + { + "ImportPath": "github.com/cznic/exp/lldb", + "Rev": "36265f1914ea00990ff0b73f72350edf9b1850df" + }, + { + "ImportPath": "github.com/cznic/fileutil", + "Rev": "1c9c88fbf552b3737c7b97e1f243860359687976" + }, + { + "ImportPath": "github.com/cznic/mathutil", + "Rev": "a804f0f2d8521e22d6adabf02cbec61dc1f9dbd2" + }, + { + "ImportPath": "github.com/cznic/ql", + "Rev": "9c77931b60c6317f94de402268bbc3e8334b71f4" + }, + { + "ImportPath": "github.com/cznic/sortutil", + "Rev": "4c7342852e65c2088c981288f2c5610d10b9f7f4" + }, + { + "ImportPath": "github.com/cznic/strutil", + "Rev": "1eb03e3cc9d345307a45ec82bd3016cde4bd4464" + }, + { + "ImportPath": "github.com/cznic/zappy", + "Rev": "47331054e4f96186e3ff772877c0443909368a45" + }, + { + "ImportPath": "github.com/golang/groupcache/lru", + "Rev": "604ed5785183e59ae2789449d89e73f3a2a77987" + }, + { + "ImportPath": "github.com/juju/ratelimit", + "Rev": "772f5c38e468398c4511514f4f6aa9a4185bc0a0" + }, + { + "ImportPath": "github.com/lib/pq", + "Comment": "go1.0-cutoff-47-g93e9980", + "Rev": "93e9980741c9e593411b94e07d5bad8cfb4809db" + }, + { + "ImportPath": "github.com/syncthing/syncthing/lib/protocol", + "Comment": "v0.12.0-beta1-119-g24c499d", + "Rev": "24c499d2822ae891c95406066456872e8d6c8164" + }, + { + "ImportPath": "github.com/thejerf/suture", + "Comment": "v1.0.1", + "Rev": "99c1f2d613756768fc4299acd9dc621e11ed3fd7" + }, + { + "ImportPath": "golang.org/x/text/transform", + "Rev": "723492b65e225eafcba054e76ba18bb9c5ac1ea2" + }, + { + "ImportPath": "golang.org/x/text/unicode/norm", + "Rev": "723492b65e225eafcba054e76ba18bb9c5ac1ea2" + } + ] +} diff --git a/Godeps/Readme b/Godeps/Readme new file mode 100644 index 000000000..4cdaa53d5 --- /dev/null +++ b/Godeps/Readme @@ -0,0 +1,5 @@ +This directory tree is generated automatically by godep. + +Please do not edit. + +See https://github.com/tools/godep for more information. diff --git a/Godeps/_workspace/.gitignore b/Godeps/_workspace/.gitignore new file mode 100644 index 000000000..f037d684e --- /dev/null +++ b/Godeps/_workspace/.gitignore @@ -0,0 +1,2 @@ +/pkg +/bin diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/.gitignore b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/.gitignore new file mode 100644 index 000000000..be64db617 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/.gitignore @@ -0,0 +1 @@ +/lz4-example/lz4-example diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/.travis.yml b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/.travis.yml new file mode 100644 index 000000000..d5870798f --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/.travis.yml @@ -0,0 +1,9 @@ +language: go + +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - tip diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/LICENSE b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/LICENSE new file mode 100644 index 000000000..0545977b7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/LICENSE @@ -0,0 +1,24 @@ +Copyright 2011-2012 Branimir Karadzic. All rights reserved. +Copyright 2013 Damian Gryski. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/README.md b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/README.md new file mode 100644 index 000000000..ef3fe1e19 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/README.md @@ -0,0 +1,71 @@ +go-lz4 +====== + +go-lz4 is port of LZ4 lossless compression algorithm to Go. The original C code +is located at: + +https://github.com/Cyan4973/lz4 + +Status +------ +[![Build Status](https://secure.travis-ci.org/bkaradzic/go-lz4.png)](http://travis-ci.org/bkaradzic/go-lz4) +[![GoDoc](https://godoc.org/github.com/bkaradzic/go-lz4?status.png)](https://godoc.org/github.com/bkaradzic/go-lz4) + +Usage +----- + + go get github.com/bkaradzic/go-lz4 + + import "github.com/bkaradzic/go-lz4" + +The package name is `lz4` + +Notes +----- + +* go-lz4 saves a uint32 with the original uncompressed length at the beginning + of the encoded buffer. They may get in the way of interoperability with + other implementations. + +Contributors +------------ + +Damian Gryski ([@dgryski](https://github.com/dgryski)) +Dustin Sallings ([@dustin](https://github.com/dustin)) + +Contact +------- + +[@bkaradzic](https://twitter.com/bkaradzic) +http://www.stuckingeometry.com + +Project page +https://github.com/bkaradzic/go-lz4 + +License +------- + +Copyright 2011-2012 Branimir Karadzic. All rights reserved. +Copyright 2013 Damian Gryski. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/fuzz.go b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/fuzz.go new file mode 100644 index 000000000..e4989de9f --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/fuzz.go @@ -0,0 +1,23 @@ +// +build gofuzz + +package lz4 + +import "encoding/binary" + +func Fuzz(data []byte) int { + + if len(data) < 4 { + return 0 + } + + ln := binary.LittleEndian.Uint32(data) + if ln > (1 << 21) { + return 0 + } + + if _, err := Decode(nil, data); err != nil { + return 0 + } + + return 1 +} diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/fuzzer/main.go b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/fuzzer/main.go new file mode 100644 index 000000000..6c8ee5754 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/fuzzer/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "math/rand" + + "github.com/bkaradzic/go-lz4" + + // lz4's API matches snappy's, so we can easily see how it performs + // lz4 "code.google.com/p/snappy-go/snappy" +) + +var input = ` +ADVENTURE I. A SCANDAL IN BOHEMIA + +I. + +To Sherlock Holmes she is always THE woman. I have seldom heard +him mention her under any other name. In his eyes she eclipses +and predominates the whole of her sex. It was not that he felt +any emotion akin to love for Irene Adler. All emotions, and that +one particularly, were abhorrent to his cold, precise but +admirably balanced mind. He was, I take it, the most perfect +reasoning and observing machine that the world has seen, but as a +lover he would have placed himself in a false position. He never +spoke of the softer passions, save with a gibe and a sneer. They +were admirable things for the observer--excellent for drawing the +veil from men's motives and actions. But for the trained reasoner +to admit such intrusions into his own delicate and finely +adjusted temperament was to introduce a distracting factor which +might throw a doubt upon all his mental results. Grit in a +sensitive instrument, or a crack in one of his own high-power +lenses, would not be more disturbing than a strong emotion in a +nature such as his. And yet there was but one woman to him, and +that woman was the late Irene Adler, of dubious and questionable +memory. + +I had seen little of Holmes lately. My marriage had drifted us +away from each other. My own complete happiness, and the +home-centred interests which rise up around the man who first +finds himself master of his own establishment, were sufficient to +absorb all my attention, while Holmes, who loathed every form of +society with his whole Bohemian soul, remained in our lodgings in +Baker Street, buried among his old books, and alternating from +week to week between cocaine and ambition, the drowsiness of the +drug, and the fierce energy of his own keen nature. He was still, +as ever, deeply attracted by the study of crime, and occupied his +immense faculties and extraordinary powers of observation in +following out those clues, and clearing up those mysteries which +had been abandoned as hopeless by the official police. From time +to time I heard some vague account of his doings: of his summons +to Odessa in the case of the Trepoff murder, of his clearing up +of the singular tragedy of the Atkinson brothers at Trincomalee, +and finally of the mission which he had accomplished so +delicately and successfully for the reigning family of Holland. +Beyond these signs of his activity, however, which I merely +shared with all the readers of the daily press, I knew little of +my former friend and companion. +` + +func main() { + + compressed, _ := lz4.Encode(nil, []byte(input)) + + modified := make([]byte, len(compressed)) + + for { + copy(modified, compressed) + for i := 0; i < 100; i++ { + modified[rand.Intn(len(compressed)-4)+4] = byte(rand.Intn(256)) + } + lz4.Decode(nil, modified) + } + +} diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/lz4-example/main.go b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/lz4-example/main.go new file mode 100644 index 000000000..ae457c860 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/lz4-example/main.go @@ -0,0 +1,94 @@ +/* + * Copyright 2011 Branimir Karadzic. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "runtime/pprof" + + lz4 "github.com/bkaradzic/go-lz4" +) + +var ( + decompress = flag.Bool("d", false, "decompress") +) + +func main() { + + var optCPUProfile = flag.String("cpuprofile", "", "profile") + flag.Parse() + + if *optCPUProfile != "" { + f, err := os.Create(*optCPUProfile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + args := flag.Args() + + var data []byte + + if len(args) < 2 { + fmt.Print("Usage: lz4 [-d] \n") + os.Exit(1) + } + + input, err := os.OpenFile(args[0], os.O_RDONLY, 0644) + if err != nil { + fmt.Printf("Failed to open input file %s\n", args[0]) + os.Exit(1) + } + defer input.Close() + + if *decompress { + data, _ = ioutil.ReadAll(input) + data, err = lz4.Decode(nil, data) + if err != nil { + fmt.Println("Failed to decode:", err) + return + } + } else { + data, _ = ioutil.ReadAll(input) + data, err = lz4.Encode(nil, data) + if err != nil { + fmt.Println("Failed to encode:", err) + return + } + } + + err = ioutil.WriteFile(args[1], data, 0644) + if err != nil { + fmt.Printf("Failed to open output file %s\n", args[1]) + os.Exit(1) + } +} diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/lz4_test.go b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/lz4_test.go new file mode 100644 index 000000000..748381737 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/lz4_test.go @@ -0,0 +1,63 @@ +package lz4 + +import ( + "bytes" + "io/ioutil" + "testing" +) + +var testfile, _ = ioutil.ReadFile("testdata/pg1661.txt") + +func roundtrip(t *testing.T, input []byte) { + + dst, err := Encode(nil, input) + if err != nil { + t.Errorf("got error during compression: %s", err) + } + + output, err := Decode(nil, dst) + + if err != nil { + t.Errorf("got error during decompress: %s", err) + } + + if !bytes.Equal(output, input) { + t.Errorf("roundtrip failed") + } +} + +func TestEmpty(t *testing.T) { + roundtrip(t, nil) +} + +func TestLengths(t *testing.T) { + + for i := 0; i < 1024; i++ { + roundtrip(t, testfile[:i]) + } + + for i := 1024; i < 4096; i += 23 { + roundtrip(t, testfile[:i]) + } +} + +func TestWords(t *testing.T) { + roundtrip(t, testfile) +} + +func BenchmarkLZ4Encode(b *testing.B) { + for i := 0; i < b.N; i++ { + Encode(nil, testfile) + } +} + +func BenchmarkLZ4Decode(b *testing.B) { + + var compressed, _ = Encode(nil, testfile) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + Decode(nil, compressed) + } +} diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/reader.go b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/reader.go new file mode 100644 index 000000000..a2b6535f6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/reader.go @@ -0,0 +1,199 @@ +/* + * Copyright 2011-2012 Branimir Karadzic. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package lz4 + +import ( + "encoding/binary" + "errors" + "io" +) + +var ( + // ErrCorrupt indicates the input was corrupt + ErrCorrupt = errors.New("corrupt input") +) + +const ( + mlBits = 4 + mlMask = (1 << mlBits) - 1 + runBits = 8 - mlBits + runMask = (1 << runBits) - 1 +) + +type decoder struct { + src []byte + dst []byte + spos uint32 + dpos uint32 + ref uint32 +} + +func (d *decoder) readByte() (uint8, error) { + if int(d.spos) == len(d.src) { + return 0, io.EOF + } + b := d.src[d.spos] + d.spos++ + return b, nil +} + +func (d *decoder) getLen() (uint32, error) { + + length := uint32(0) + ln, err := d.readByte() + if err != nil { + return 0, ErrCorrupt + } + for ln == 255 { + length += 255 + ln, err = d.readByte() + if err != nil { + return 0, ErrCorrupt + } + } + length += uint32(ln) + + return length, nil +} + +func (d *decoder) cp(length, decr uint32) { + + if int(d.ref+length) < int(d.dpos) { + copy(d.dst[d.dpos:], d.dst[d.ref:d.ref+length]) + } else { + for ii := uint32(0); ii < length; ii++ { + d.dst[d.dpos+ii] = d.dst[d.ref+ii] + } + } + d.dpos += length + d.ref += length - decr +} + +func (d *decoder) finish(err error) error { + if err == io.EOF { + return nil + } + + return err +} + +// Decode returns the decoded form of src. The returned slice may be a +// subslice of dst if it was large enough to hold the entire decoded block. +func Decode(dst, src []byte) ([]byte, error) { + + if len(src) < 4 { + return nil, ErrCorrupt + } + + uncompressedLen := binary.LittleEndian.Uint32(src) + + if uncompressedLen == 0 { + return nil, nil + } + + if uncompressedLen > MaxInputSize { + return nil, ErrTooLarge + } + + if dst == nil || len(dst) < int(uncompressedLen) { + dst = make([]byte, uncompressedLen) + } + + d := decoder{src: src, dst: dst[:uncompressedLen], spos: 4} + + decr := []uint32{0, 3, 2, 3} + + for { + code, err := d.readByte() + if err != nil { + return d.dst, d.finish(err) + } + + length := uint32(code >> mlBits) + if length == runMask { + ln, err := d.getLen() + if err != nil { + return nil, ErrCorrupt + } + length += ln + } + + if int(d.spos+length) > len(d.src) || int(d.dpos+length) > len(d.dst) { + return nil, ErrCorrupt + } + + for ii := uint32(0); ii < length; ii++ { + d.dst[d.dpos+ii] = d.src[d.spos+ii] + } + + d.spos += length + d.dpos += length + + if int(d.spos) == len(d.src) { + return d.dst, nil + } + + if int(d.spos+2) >= len(d.src) { + return nil, ErrCorrupt + } + + back := uint32(d.src[d.spos]) | uint32(d.src[d.spos+1])<<8 + + if back > d.dpos { + return nil, ErrCorrupt + } + + d.spos += 2 + d.ref = d.dpos - back + + length = uint32(code & mlMask) + if length == mlMask { + ln, err := d.getLen() + if err != nil { + return nil, ErrCorrupt + } + length += ln + } + + literal := d.dpos - d.ref + + if literal < 4 { + if int(d.dpos+4) > len(d.dst) { + return nil, ErrCorrupt + } + + d.cp(4, decr[literal]) + } else { + length += 4 + } + + if d.dpos+length > uncompressedLen { + return nil, ErrCorrupt + } + + d.cp(length, 0) + } +} diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/testdata/pg1661.txt b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/testdata/pg1661.txt new file mode 100644 index 000000000..c4c313050 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/testdata/pg1661.txt @@ -0,0 +1,13052 @@ +Project Gutenberg's The Adventures of Sherlock Holmes, by Arthur Conan Doyle + +This eBook is for the use of anyone anywhere at no cost and with +almost no restrictions whatsoever. You may copy it, give it away or +re-use it under the terms of the Project Gutenberg License included +with this eBook or online at www.gutenberg.net + + +Title: The Adventures of Sherlock Holmes + +Author: Arthur Conan Doyle + +Posting Date: April 18, 2011 [EBook #1661] +First Posted: November 29, 2002 + +Language: English + + +*** START OF THIS PROJECT GUTENBERG EBOOK THE ADVENTURES OF SHERLOCK HOLMES *** + + + + +Produced by an anonymous Project Gutenberg volunteer and Jose Menendez + + + + + + + + + +THE ADVENTURES OF SHERLOCK HOLMES + +by + +SIR ARTHUR CONAN DOYLE + + + + I. A Scandal in Bohemia + II. The Red-headed League + III. A Case of Identity + IV. The Boscombe Valley Mystery + V. The Five Orange Pips + VI. The Man with the Twisted Lip + VII. The Adventure of the Blue Carbuncle +VIII. The Adventure of the Speckled Band + IX. The Adventure of the Engineer's Thumb + X. The Adventure of the Noble Bachelor + XI. The Adventure of the Beryl Coronet + XII. The Adventure of the Copper Beeches + + + + +ADVENTURE I. A SCANDAL IN BOHEMIA + +I. + +To Sherlock Holmes she is always THE woman. I have seldom heard +him mention her under any other name. In his eyes she eclipses +and predominates the whole of her sex. It was not that he felt +any emotion akin to love for Irene Adler. All emotions, and that +one particularly, were abhorrent to his cold, precise but +admirably balanced mind. He was, I take it, the most perfect +reasoning and observing machine that the world has seen, but as a +lover he would have placed himself in a false position. He never +spoke of the softer passions, save with a gibe and a sneer. They +were admirable things for the observer--excellent for drawing the +veil from men's motives and actions. But for the trained reasoner +to admit such intrusions into his own delicate and finely +adjusted temperament was to introduce a distracting factor which +might throw a doubt upon all his mental results. Grit in a +sensitive instrument, or a crack in one of his own high-power +lenses, would not be more disturbing than a strong emotion in a +nature such as his. And yet there was but one woman to him, and +that woman was the late Irene Adler, of dubious and questionable +memory. + +I had seen little of Holmes lately. My marriage had drifted us +away from each other. My own complete happiness, and the +home-centred interests which rise up around the man who first +finds himself master of his own establishment, were sufficient to +absorb all my attention, while Holmes, who loathed every form of +society with his whole Bohemian soul, remained in our lodgings in +Baker Street, buried among his old books, and alternating from +week to week between cocaine and ambition, the drowsiness of the +drug, and the fierce energy of his own keen nature. He was still, +as ever, deeply attracted by the study of crime, and occupied his +immense faculties and extraordinary powers of observation in +following out those clues, and clearing up those mysteries which +had been abandoned as hopeless by the official police. From time +to time I heard some vague account of his doings: of his summons +to Odessa in the case of the Trepoff murder, of his clearing up +of the singular tragedy of the Atkinson brothers at Trincomalee, +and finally of the mission which he had accomplished so +delicately and successfully for the reigning family of Holland. +Beyond these signs of his activity, however, which I merely +shared with all the readers of the daily press, I knew little of +my former friend and companion. + +One night--it was on the twentieth of March, 1888--I was +returning from a journey to a patient (for I had now returned to +civil practice), when my way led me through Baker Street. As I +passed the well-remembered door, which must always be associated +in my mind with my wooing, and with the dark incidents of the +Study in Scarlet, I was seized with a keen desire to see Holmes +again, and to know how he was employing his extraordinary powers. +His rooms were brilliantly lit, and, even as I looked up, I saw +his tall, spare figure pass twice in a dark silhouette against +the blind. He was pacing the room swiftly, eagerly, with his head +sunk upon his chest and his hands clasped behind him. To me, who +knew his every mood and habit, his attitude and manner told their +own story. He was at work again. He had risen out of his +drug-created dreams and was hot upon the scent of some new +problem. I rang the bell and was shown up to the chamber which +had formerly been in part my own. + +His manner was not effusive. It seldom was; but he was glad, I +think, to see me. With hardly a word spoken, but with a kindly +eye, he waved me to an armchair, threw across his case of cigars, +and indicated a spirit case and a gasogene in the corner. Then he +stood before the fire and looked me over in his singular +introspective fashion. + +"Wedlock suits you," he remarked. "I think, Watson, that you have +put on seven and a half pounds since I saw you." + +"Seven!" I answered. + +"Indeed, I should have thought a little more. Just a trifle more, +I fancy, Watson. And in practice again, I observe. You did not +tell me that you intended to go into harness." + +"Then, how do you know?" + +"I see it, I deduce it. How do I know that you have been getting +yourself very wet lately, and that you have a most clumsy and +careless servant girl?" + +"My dear Holmes," said I, "this is too much. You would certainly +have been burned, had you lived a few centuries ago. It is true +that I had a country walk on Thursday and came home in a dreadful +mess, but as I have changed my clothes I can't imagine how you +deduce it. As to Mary Jane, she is incorrigible, and my wife has +given her notice, but there, again, I fail to see how you work it +out." + +He chuckled to himself and rubbed his long, nervous hands +together. + +"It is simplicity itself," said he; "my eyes tell me that on the +inside of your left shoe, just where the firelight strikes it, +the leather is scored by six almost parallel cuts. Obviously they +have been caused by someone who has very carelessly scraped round +the edges of the sole in order to remove crusted mud from it. +Hence, you see, my double deduction that you had been out in vile +weather, and that you had a particularly malignant boot-slitting +specimen of the London slavey. As to your practice, if a +gentleman walks into my rooms smelling of iodoform, with a black +mark of nitrate of silver upon his right forefinger, and a bulge +on the right side of his top-hat to show where he has secreted +his stethoscope, I must be dull, indeed, if I do not pronounce +him to be an active member of the medical profession." + +I could not help laughing at the ease with which he explained his +process of deduction. "When I hear you give your reasons," I +remarked, "the thing always appears to me to be so ridiculously +simple that I could easily do it myself, though at each +successive instance of your reasoning I am baffled until you +explain your process. And yet I believe that my eyes are as good +as yours." + +"Quite so," he answered, lighting a cigarette, and throwing +himself down into an armchair. "You see, but you do not observe. +The distinction is clear. For example, you have frequently seen +the steps which lead up from the hall to this room." + +"Frequently." + +"How often?" + +"Well, some hundreds of times." + +"Then how many are there?" + +"How many? I don't know." + +"Quite so! You have not observed. And yet you have seen. That is +just my point. Now, I know that there are seventeen steps, +because I have both seen and observed. By-the-way, since you are +interested in these little problems, and since you are good +enough to chronicle one or two of my trifling experiences, you +may be interested in this." He threw over a sheet of thick, +pink-tinted note-paper which had been lying open upon the table. +"It came by the last post," said he. "Read it aloud." + +The note was undated, and without either signature or address. + +"There will call upon you to-night, at a quarter to eight +o'clock," it said, "a gentleman who desires to consult you upon a +matter of the very deepest moment. Your recent services to one of +the royal houses of Europe have shown that you are one who may +safely be trusted with matters which are of an importance which +can hardly be exaggerated. This account of you we have from all +quarters received. Be in your chamber then at that hour, and do +not take it amiss if your visitor wear a mask." + +"This is indeed a mystery," I remarked. "What do you imagine that +it means?" + +"I have no data yet. It is a capital mistake to theorize before +one has data. Insensibly one begins to twist facts to suit +theories, instead of theories to suit facts. But the note itself. +What do you deduce from it?" + +I carefully examined the writing, and the paper upon which it was +written. + +"The man who wrote it was presumably well to do," I remarked, +endeavouring to imitate my companion's processes. "Such paper +could not be bought under half a crown a packet. It is peculiarly +strong and stiff." + +"Peculiar--that is the very word," said Holmes. "It is not an +English paper at all. Hold it up to the light." + +I did so, and saw a large "E" with a small "g," a "P," and a +large "G" with a small "t" woven into the texture of the paper. + +"What do you make of that?" asked Holmes. + +"The name of the maker, no doubt; or his monogram, rather." + +"Not at all. The 'G' with the small 't' stands for +'Gesellschaft,' which is the German for 'Company.' It is a +customary contraction like our 'Co.' 'P,' of course, stands for +'Papier.' Now for the 'Eg.' Let us glance at our Continental +Gazetteer." He took down a heavy brown volume from his shelves. +"Eglow, Eglonitz--here we are, Egria. It is in a German-speaking +country--in Bohemia, not far from Carlsbad. 'Remarkable as being +the scene of the death of Wallenstein, and for its numerous +glass-factories and paper-mills.' Ha, ha, my boy, what do you +make of that?" His eyes sparkled, and he sent up a great blue +triumphant cloud from his cigarette. + +"The paper was made in Bohemia," I said. + +"Precisely. And the man who wrote the note is a German. Do you +note the peculiar construction of the sentence--'This account of +you we have from all quarters received.' A Frenchman or Russian +could not have written that. It is the German who is so +uncourteous to his verbs. It only remains, therefore, to discover +what is wanted by this German who writes upon Bohemian paper and +prefers wearing a mask to showing his face. And here he comes, if +I am not mistaken, to resolve all our doubts." + +As he spoke there was the sharp sound of horses' hoofs and +grating wheels against the curb, followed by a sharp pull at the +bell. Holmes whistled. + +"A pair, by the sound," said he. "Yes," he continued, glancing +out of the window. "A nice little brougham and a pair of +beauties. A hundred and fifty guineas apiece. There's money in +this case, Watson, if there is nothing else." + +"I think that I had better go, Holmes." + +"Not a bit, Doctor. Stay where you are. I am lost without my +Boswell. And this promises to be interesting. It would be a pity +to miss it." + +"But your client--" + +"Never mind him. I may want your help, and so may he. Here he +comes. Sit down in that armchair, Doctor, and give us your best +attention." + +A slow and heavy step, which had been heard upon the stairs and +in the passage, paused immediately outside the door. Then there +was a loud and authoritative tap. + +"Come in!" said Holmes. + +A man entered who could hardly have been less than six feet six +inches in height, with the chest and limbs of a Hercules. His +dress was rich with a richness which would, in England, be looked +upon as akin to bad taste. Heavy bands of astrakhan were slashed +across the sleeves and fronts of his double-breasted coat, while +the deep blue cloak which was thrown over his shoulders was lined +with flame-coloured silk and secured at the neck with a brooch +which consisted of a single flaming beryl. Boots which extended +halfway up his calves, and which were trimmed at the tops with +rich brown fur, completed the impression of barbaric opulence +which was suggested by his whole appearance. He carried a +broad-brimmed hat in his hand, while he wore across the upper +part of his face, extending down past the cheekbones, a black +vizard mask, which he had apparently adjusted that very moment, +for his hand was still raised to it as he entered. From the lower +part of the face he appeared to be a man of strong character, +with a thick, hanging lip, and a long, straight chin suggestive +of resolution pushed to the length of obstinacy. + +"You had my note?" he asked with a deep harsh voice and a +strongly marked German accent. "I told you that I would call." He +looked from one to the other of us, as if uncertain which to +address. + +"Pray take a seat," said Holmes. "This is my friend and +colleague, Dr. Watson, who is occasionally good enough to help me +in my cases. Whom have I the honour to address?" + +"You may address me as the Count Von Kramm, a Bohemian nobleman. +I understand that this gentleman, your friend, is a man of honour +and discretion, whom I may trust with a matter of the most +extreme importance. If not, I should much prefer to communicate +with you alone." + +I rose to go, but Holmes caught me by the wrist and pushed me +back into my chair. "It is both, or none," said he. "You may say +before this gentleman anything which you may say to me." + +The Count shrugged his broad shoulders. "Then I must begin," said +he, "by binding you both to absolute secrecy for two years; at +the end of that time the matter will be of no importance. At +present it is not too much to say that it is of such weight it +may have an influence upon European history." + +"I promise," said Holmes. + +"And I." + +"You will excuse this mask," continued our strange visitor. "The +august person who employs me wishes his agent to be unknown to +you, and I may confess at once that the title by which I have +just called myself is not exactly my own." + +"I was aware of it," said Holmes dryly. + +"The circumstances are of great delicacy, and every precaution +has to be taken to quench what might grow to be an immense +scandal and seriously compromise one of the reigning families of +Europe. To speak plainly, the matter implicates the great House +of Ormstein, hereditary kings of Bohemia." + +"I was also aware of that," murmured Holmes, settling himself +down in his armchair and closing his eyes. + +Our visitor glanced with some apparent surprise at the languid, +lounging figure of the man who had been no doubt depicted to him +as the most incisive reasoner and most energetic agent in Europe. +Holmes slowly reopened his eyes and looked impatiently at his +gigantic client. + +"If your Majesty would condescend to state your case," he +remarked, "I should be better able to advise you." + +The man sprang from his chair and paced up and down the room in +uncontrollable agitation. Then, with a gesture of desperation, he +tore the mask from his face and hurled it upon the ground. "You +are right," he cried; "I am the King. Why should I attempt to +conceal it?" + +"Why, indeed?" murmured Holmes. "Your Majesty had not spoken +before I was aware that I was addressing Wilhelm Gottsreich +Sigismond von Ormstein, Grand Duke of Cassel-Felstein, and +hereditary King of Bohemia." + +"But you can understand," said our strange visitor, sitting down +once more and passing his hand over his high white forehead, "you +can understand that I am not accustomed to doing such business in +my own person. Yet the matter was so delicate that I could not +confide it to an agent without putting myself in his power. I +have come incognito from Prague for the purpose of consulting +you." + +"Then, pray consult," said Holmes, shutting his eyes once more. + +"The facts are briefly these: Some five years ago, during a +lengthy visit to Warsaw, I made the acquaintance of the well-known +adventuress, Irene Adler. The name is no doubt familiar to you." + +"Kindly look her up in my index, Doctor," murmured Holmes without +opening his eyes. For many years he had adopted a system of +docketing all paragraphs concerning men and things, so that it +was difficult to name a subject or a person on which he could not +at once furnish information. In this case I found her biography +sandwiched in between that of a Hebrew rabbi and that of a +staff-commander who had written a monograph upon the deep-sea +fishes. + +"Let me see!" said Holmes. "Hum! Born in New Jersey in the year +1858. Contralto--hum! La Scala, hum! Prima donna Imperial Opera +of Warsaw--yes! Retired from operatic stage--ha! Living in +London--quite so! Your Majesty, as I understand, became entangled +with this young person, wrote her some compromising letters, and +is now desirous of getting those letters back." + +"Precisely so. But how--" + +"Was there a secret marriage?" + +"None." + +"No legal papers or certificates?" + +"None." + +"Then I fail to follow your Majesty. If this young person should +produce her letters for blackmailing or other purposes, how is +she to prove their authenticity?" + +"There is the writing." + +"Pooh, pooh! Forgery." + +"My private note-paper." + +"Stolen." + +"My own seal." + +"Imitated." + +"My photograph." + +"Bought." + +"We were both in the photograph." + +"Oh, dear! That is very bad! Your Majesty has indeed committed an +indiscretion." + +"I was mad--insane." + +"You have compromised yourself seriously." + +"I was only Crown Prince then. I was young. I am but thirty now." + +"It must be recovered." + +"We have tried and failed." + +"Your Majesty must pay. It must be bought." + +"She will not sell." + +"Stolen, then." + +"Five attempts have been made. Twice burglars in my pay ransacked +her house. Once we diverted her luggage when she travelled. Twice +she has been waylaid. There has been no result." + +"No sign of it?" + +"Absolutely none." + +Holmes laughed. "It is quite a pretty little problem," said he. + +"But a very serious one to me," returned the King reproachfully. + +"Very, indeed. And what does she propose to do with the +photograph?" + +"To ruin me." + +"But how?" + +"I am about to be married." + +"So I have heard." + +"To Clotilde Lothman von Saxe-Meningen, second daughter of the +King of Scandinavia. You may know the strict principles of her +family. She is herself the very soul of delicacy. A shadow of a +doubt as to my conduct would bring the matter to an end." + +"And Irene Adler?" + +"Threatens to send them the photograph. And she will do it. I +know that she will do it. You do not know her, but she has a soul +of steel. She has the face of the most beautiful of women, and +the mind of the most resolute of men. Rather than I should marry +another woman, there are no lengths to which she would not +go--none." + +"You are sure that she has not sent it yet?" + +"I am sure." + +"And why?" + +"Because she has said that she would send it on the day when the +betrothal was publicly proclaimed. That will be next Monday." + +"Oh, then we have three days yet," said Holmes with a yawn. "That +is very fortunate, as I have one or two matters of importance to +look into just at present. Your Majesty will, of course, stay in +London for the present?" + +"Certainly. You will find me at the Langham under the name of the +Count Von Kramm." + +"Then I shall drop you a line to let you know how we progress." + +"Pray do so. I shall be all anxiety." + +"Then, as to money?" + +"You have carte blanche." + +"Absolutely?" + +"I tell you that I would give one of the provinces of my kingdom +to have that photograph." + +"And for present expenses?" + +The King took a heavy chamois leather bag from under his cloak +and laid it on the table. + +"There are three hundred pounds in gold and seven hundred in +notes," he said. + +Holmes scribbled a receipt upon a sheet of his note-book and +handed it to him. + +"And Mademoiselle's address?" he asked. + +"Is Briony Lodge, Serpentine Avenue, St. John's Wood." + +Holmes took a note of it. "One other question," said he. "Was the +photograph a cabinet?" + +"It was." + +"Then, good-night, your Majesty, and I trust that we shall soon +have some good news for you. And good-night, Watson," he added, +as the wheels of the royal brougham rolled down the street. "If +you will be good enough to call to-morrow afternoon at three +o'clock I should like to chat this little matter over with you." + + +II. + +At three o'clock precisely I was at Baker Street, but Holmes had +not yet returned. The landlady informed me that he had left the +house shortly after eight o'clock in the morning. I sat down +beside the fire, however, with the intention of awaiting him, +however long he might be. I was already deeply interested in his +inquiry, for, though it was surrounded by none of the grim and +strange features which were associated with the two crimes which +I have already recorded, still, the nature of the case and the +exalted station of his client gave it a character of its own. +Indeed, apart from the nature of the investigation which my +friend had on hand, there was something in his masterly grasp of +a situation, and his keen, incisive reasoning, which made it a +pleasure to me to study his system of work, and to follow the +quick, subtle methods by which he disentangled the most +inextricable mysteries. So accustomed was I to his invariable +success that the very possibility of his failing had ceased to +enter into my head. + +It was close upon four before the door opened, and a +drunken-looking groom, ill-kempt and side-whiskered, with an +inflamed face and disreputable clothes, walked into the room. +Accustomed as I was to my friend's amazing powers in the use of +disguises, I had to look three times before I was certain that it +was indeed he. With a nod he vanished into the bedroom, whence he +emerged in five minutes tweed-suited and respectable, as of old. +Putting his hands into his pockets, he stretched out his legs in +front of the fire and laughed heartily for some minutes. + +"Well, really!" he cried, and then he choked and laughed again +until he was obliged to lie back, limp and helpless, in the +chair. + +"What is it?" + +"It's quite too funny. I am sure you could never guess how I +employed my morning, or what I ended by doing." + +"I can't imagine. I suppose that you have been watching the +habits, and perhaps the house, of Miss Irene Adler." + +"Quite so; but the sequel was rather unusual. I will tell you, +however. I left the house a little after eight o'clock this +morning in the character of a groom out of work. There is a +wonderful sympathy and freemasonry among horsey men. Be one of +them, and you will know all that there is to know. I soon found +Briony Lodge. It is a bijou villa, with a garden at the back, but +built out in front right up to the road, two stories. Chubb lock +to the door. Large sitting-room on the right side, well +furnished, with long windows almost to the floor, and those +preposterous English window fasteners which a child could open. +Behind there was nothing remarkable, save that the passage window +could be reached from the top of the coach-house. I walked round +it and examined it closely from every point of view, but without +noting anything else of interest. + +"I then lounged down the street and found, as I expected, that +there was a mews in a lane which runs down by one wall of the +garden. I lent the ostlers a hand in rubbing down their horses, +and received in exchange twopence, a glass of half and half, two +fills of shag tobacco, and as much information as I could desire +about Miss Adler, to say nothing of half a dozen other people in +the neighbourhood in whom I was not in the least interested, but +whose biographies I was compelled to listen to." + +"And what of Irene Adler?" I asked. + +"Oh, she has turned all the men's heads down in that part. She is +the daintiest thing under a bonnet on this planet. So say the +Serpentine-mews, to a man. She lives quietly, sings at concerts, +drives out at five every day, and returns at seven sharp for +dinner. Seldom goes out at other times, except when she sings. +Has only one male visitor, but a good deal of him. He is dark, +handsome, and dashing, never calls less than once a day, and +often twice. He is a Mr. Godfrey Norton, of the Inner Temple. See +the advantages of a cabman as a confidant. They had driven him +home a dozen times from Serpentine-mews, and knew all about him. +When I had listened to all they had to tell, I began to walk up +and down near Briony Lodge once more, and to think over my plan +of campaign. + +"This Godfrey Norton was evidently an important factor in the +matter. He was a lawyer. That sounded ominous. What was the +relation between them, and what the object of his repeated +visits? Was she his client, his friend, or his mistress? If the +former, she had probably transferred the photograph to his +keeping. If the latter, it was less likely. On the issue of this +question depended whether I should continue my work at Briony +Lodge, or turn my attention to the gentleman's chambers in the +Temple. It was a delicate point, and it widened the field of my +inquiry. I fear that I bore you with these details, but I have to +let you see my little difficulties, if you are to understand the +situation." + +"I am following you closely," I answered. + +"I was still balancing the matter in my mind when a hansom cab +drove up to Briony Lodge, and a gentleman sprang out. He was a +remarkably handsome man, dark, aquiline, and moustached--evidently +the man of whom I had heard. He appeared to be in a +great hurry, shouted to the cabman to wait, and brushed past the +maid who opened the door with the air of a man who was thoroughly +at home. + +"He was in the house about half an hour, and I could catch +glimpses of him in the windows of the sitting-room, pacing up and +down, talking excitedly, and waving his arms. Of her I could see +nothing. Presently he emerged, looking even more flurried than +before. As he stepped up to the cab, he pulled a gold watch from +his pocket and looked at it earnestly, 'Drive like the devil,' he +shouted, 'first to Gross & Hankey's in Regent Street, and then to +the Church of St. Monica in the Edgeware Road. Half a guinea if +you do it in twenty minutes!' + +"Away they went, and I was just wondering whether I should not do +well to follow them when up the lane came a neat little landau, +the coachman with his coat only half-buttoned, and his tie under +his ear, while all the tags of his harness were sticking out of +the buckles. It hadn't pulled up before she shot out of the hall +door and into it. I only caught a glimpse of her at the moment, +but she was a lovely woman, with a face that a man might die for. + +"'The Church of St. Monica, John,' she cried, 'and half a +sovereign if you reach it in twenty minutes.' + +"This was quite too good to lose, Watson. I was just balancing +whether I should run for it, or whether I should perch behind her +landau when a cab came through the street. The driver looked +twice at such a shabby fare, but I jumped in before he could +object. 'The Church of St. Monica,' said I, 'and half a sovereign +if you reach it in twenty minutes.' It was twenty-five minutes to +twelve, and of course it was clear enough what was in the wind. + +"My cabby drove fast. I don't think I ever drove faster, but the +others were there before us. The cab and the landau with their +steaming horses were in front of the door when I arrived. I paid +the man and hurried into the church. There was not a soul there +save the two whom I had followed and a surpliced clergyman, who +seemed to be expostulating with them. They were all three +standing in a knot in front of the altar. I lounged up the side +aisle like any other idler who has dropped into a church. +Suddenly, to my surprise, the three at the altar faced round to +me, and Godfrey Norton came running as hard as he could towards +me. + +"'Thank God,' he cried. 'You'll do. Come! Come!' + +"'What then?' I asked. + +"'Come, man, come, only three minutes, or it won't be legal.' + +"I was half-dragged up to the altar, and before I knew where I was +I found myself mumbling responses which were whispered in my ear, +and vouching for things of which I knew nothing, and generally +assisting in the secure tying up of Irene Adler, spinster, to +Godfrey Norton, bachelor. It was all done in an instant, and +there was the gentleman thanking me on the one side and the lady +on the other, while the clergyman beamed on me in front. It was +the most preposterous position in which I ever found myself in my +life, and it was the thought of it that started me laughing just +now. It seems that there had been some informality about their +license, that the clergyman absolutely refused to marry them +without a witness of some sort, and that my lucky appearance +saved the bridegroom from having to sally out into the streets in +search of a best man. The bride gave me a sovereign, and I mean +to wear it on my watch-chain in memory of the occasion." + +"This is a very unexpected turn of affairs," said I; "and what +then?" + +"Well, I found my plans very seriously menaced. It looked as if +the pair might take an immediate departure, and so necessitate +very prompt and energetic measures on my part. At the church +door, however, they separated, he driving back to the Temple, and +she to her own house. 'I shall drive out in the park at five as +usual,' she said as she left him. I heard no more. They drove +away in different directions, and I went off to make my own +arrangements." + +"Which are?" + +"Some cold beef and a glass of beer," he answered, ringing the +bell. "I have been too busy to think of food, and I am likely to +be busier still this evening. By the way, Doctor, I shall want +your co-operation." + +"I shall be delighted." + +"You don't mind breaking the law?" + +"Not in the least." + +"Nor running a chance of arrest?" + +"Not in a good cause." + +"Oh, the cause is excellent!" + +"Then I am your man." + +"I was sure that I might rely on you." + +"But what is it you wish?" + +"When Mrs. Turner has brought in the tray I will make it clear to +you. Now," he said as he turned hungrily on the simple fare that +our landlady had provided, "I must discuss it while I eat, for I +have not much time. It is nearly five now. In two hours we must +be on the scene of action. Miss Irene, or Madame, rather, returns +from her drive at seven. We must be at Briony Lodge to meet her." + +"And what then?" + +"You must leave that to me. I have already arranged what is to +occur. There is only one point on which I must insist. You must +not interfere, come what may. You understand?" + +"I am to be neutral?" + +"To do nothing whatever. There will probably be some small +unpleasantness. Do not join in it. It will end in my being +conveyed into the house. Four or five minutes afterwards the +sitting-room window will open. You are to station yourself close +to that open window." + +"Yes." + +"You are to watch me, for I will be visible to you." + +"Yes." + +"And when I raise my hand--so--you will throw into the room what +I give you to throw, and will, at the same time, raise the cry of +fire. You quite follow me?" + +"Entirely." + +"It is nothing very formidable," he said, taking a long cigar-shaped +roll from his pocket. "It is an ordinary plumber's smoke-rocket, +fitted with a cap at either end to make it self-lighting. +Your task is confined to that. When you raise your cry of fire, +it will be taken up by quite a number of people. You may then +walk to the end of the street, and I will rejoin you in ten +minutes. I hope that I have made myself clear?" + +"I am to remain neutral, to get near the window, to watch you, +and at the signal to throw in this object, then to raise the cry +of fire, and to wait you at the corner of the street." + +"Precisely." + +"Then you may entirely rely on me." + +"That is excellent. I think, perhaps, it is almost time that I +prepare for the new role I have to play." + +He disappeared into his bedroom and returned in a few minutes in +the character of an amiable and simple-minded Nonconformist +clergyman. His broad black hat, his baggy trousers, his white +tie, his sympathetic smile, and general look of peering and +benevolent curiosity were such as Mr. John Hare alone could have +equalled. It was not merely that Holmes changed his costume. His +expression, his manner, his very soul seemed to vary with every +fresh part that he assumed. The stage lost a fine actor, even as +science lost an acute reasoner, when he became a specialist in +crime. + +It was a quarter past six when we left Baker Street, and it still +wanted ten minutes to the hour when we found ourselves in +Serpentine Avenue. It was already dusk, and the lamps were just +being lighted as we paced up and down in front of Briony Lodge, +waiting for the coming of its occupant. The house was just such +as I had pictured it from Sherlock Holmes' succinct description, +but the locality appeared to be less private than I expected. On +the contrary, for a small street in a quiet neighbourhood, it was +remarkably animated. There was a group of shabbily dressed men +smoking and laughing in a corner, a scissors-grinder with his +wheel, two guardsmen who were flirting with a nurse-girl, and +several well-dressed young men who were lounging up and down with +cigars in their mouths. + +"You see," remarked Holmes, as we paced to and fro in front of +the house, "this marriage rather simplifies matters. The +photograph becomes a double-edged weapon now. The chances are +that she would be as averse to its being seen by Mr. Godfrey +Norton, as our client is to its coming to the eyes of his +princess. Now the question is, Where are we to find the +photograph?" + +"Where, indeed?" + +"It is most unlikely that she carries it about with her. It is +cabinet size. Too large for easy concealment about a woman's +dress. She knows that the King is capable of having her waylaid +and searched. Two attempts of the sort have already been made. We +may take it, then, that she does not carry it about with her." + +"Where, then?" + +"Her banker or her lawyer. There is that double possibility. But +I am inclined to think neither. Women are naturally secretive, +and they like to do their own secreting. Why should she hand it +over to anyone else? She could trust her own guardianship, but +she could not tell what indirect or political influence might be +brought to bear upon a business man. Besides, remember that she +had resolved to use it within a few days. It must be where she +can lay her hands upon it. It must be in her own house." + +"But it has twice been burgled." + +"Pshaw! They did not know how to look." + +"But how will you look?" + +"I will not look." + +"What then?" + +"I will get her to show me." + +"But she will refuse." + +"She will not be able to. But I hear the rumble of wheels. It is +her carriage. Now carry out my orders to the letter." + +As he spoke the gleam of the side-lights of a carriage came round +the curve of the avenue. It was a smart little landau which +rattled up to the door of Briony Lodge. As it pulled up, one of +the loafing men at the corner dashed forward to open the door in +the hope of earning a copper, but was elbowed away by another +loafer, who had rushed up with the same intention. A fierce +quarrel broke out, which was increased by the two guardsmen, who +took sides with one of the loungers, and by the scissors-grinder, +who was equally hot upon the other side. A blow was struck, and +in an instant the lady, who had stepped from her carriage, was +the centre of a little knot of flushed and struggling men, who +struck savagely at each other with their fists and sticks. Holmes +dashed into the crowd to protect the lady; but just as he reached +her he gave a cry and dropped to the ground, with the blood +running freely down his face. At his fall the guardsmen took to +their heels in one direction and the loungers in the other, while +a number of better-dressed people, who had watched the scuffle +without taking part in it, crowded in to help the lady and to +attend to the injured man. Irene Adler, as I will still call her, +had hurried up the steps; but she stood at the top with her +superb figure outlined against the lights of the hall, looking +back into the street. + +"Is the poor gentleman much hurt?" she asked. + +"He is dead," cried several voices. + +"No, no, there's life in him!" shouted another. "But he'll be +gone before you can get him to hospital." + +"He's a brave fellow," said a woman. "They would have had the +lady's purse and watch if it hadn't been for him. They were a +gang, and a rough one, too. Ah, he's breathing now." + +"He can't lie in the street. May we bring him in, marm?" + +"Surely. Bring him into the sitting-room. There is a comfortable +sofa. This way, please!" + +Slowly and solemnly he was borne into Briony Lodge and laid out +in the principal room, while I still observed the proceedings +from my post by the window. The lamps had been lit, but the +blinds had not been drawn, so that I could see Holmes as he lay +upon the couch. I do not know whether he was seized with +compunction at that moment for the part he was playing, but I +know that I never felt more heartily ashamed of myself in my life +than when I saw the beautiful creature against whom I was +conspiring, or the grace and kindliness with which she waited +upon the injured man. And yet it would be the blackest treachery +to Holmes to draw back now from the part which he had intrusted +to me. I hardened my heart, and took the smoke-rocket from under +my ulster. After all, I thought, we are not injuring her. We are +but preventing her from injuring another. + +Holmes had sat up upon the couch, and I saw him motion like a man +who is in need of air. A maid rushed across and threw open the +window. At the same instant I saw him raise his hand and at the +signal I tossed my rocket into the room with a cry of "Fire!" The +word was no sooner out of my mouth than the whole crowd of +spectators, well dressed and ill--gentlemen, ostlers, and +servant-maids--joined in a general shriek of "Fire!" Thick clouds +of smoke curled through the room and out at the open window. I +caught a glimpse of rushing figures, and a moment later the voice +of Holmes from within assuring them that it was a false alarm. +Slipping through the shouting crowd I made my way to the corner +of the street, and in ten minutes was rejoiced to find my +friend's arm in mine, and to get away from the scene of uproar. +He walked swiftly and in silence for some few minutes until we +had turned down one of the quiet streets which lead towards the +Edgeware Road. + +"You did it very nicely, Doctor," he remarked. "Nothing could +have been better. It is all right." + +"You have the photograph?" + +"I know where it is." + +"And how did you find out?" + +"She showed me, as I told you she would." + +"I am still in the dark." + +"I do not wish to make a mystery," said he, laughing. "The matter +was perfectly simple. You, of course, saw that everyone in the +street was an accomplice. They were all engaged for the evening." + +"I guessed as much." + +"Then, when the row broke out, I had a little moist red paint in +the palm of my hand. I rushed forward, fell down, clapped my hand +to my face, and became a piteous spectacle. It is an old trick." + +"That also I could fathom." + +"Then they carried me in. She was bound to have me in. What else +could she do? And into her sitting-room, which was the very room +which I suspected. It lay between that and her bedroom, and I was +determined to see which. They laid me on a couch, I motioned for +air, they were compelled to open the window, and you had your +chance." + +"How did that help you?" + +"It was all-important. When a woman thinks that her house is on +fire, her instinct is at once to rush to the thing which she +values most. It is a perfectly overpowering impulse, and I have +more than once taken advantage of it. In the case of the +Darlington substitution scandal it was of use to me, and also in +the Arnsworth Castle business. A married woman grabs at her baby; +an unmarried one reaches for her jewel-box. Now it was clear to +me that our lady of to-day had nothing in the house more precious +to her than what we are in quest of. She would rush to secure it. +The alarm of fire was admirably done. The smoke and shouting were +enough to shake nerves of steel. She responded beautifully. The +photograph is in a recess behind a sliding panel just above the +right bell-pull. She was there in an instant, and I caught a +glimpse of it as she half-drew it out. When I cried out that it +was a false alarm, she replaced it, glanced at the rocket, rushed +from the room, and I have not seen her since. I rose, and, making +my excuses, escaped from the house. I hesitated whether to +attempt to secure the photograph at once; but the coachman had +come in, and as he was watching me narrowly it seemed safer to +wait. A little over-precipitance may ruin all." + +"And now?" I asked. + +"Our quest is practically finished. I shall call with the King +to-morrow, and with you, if you care to come with us. We will be +shown into the sitting-room to wait for the lady, but it is +probable that when she comes she may find neither us nor the +photograph. It might be a satisfaction to his Majesty to regain +it with his own hands." + +"And when will you call?" + +"At eight in the morning. She will not be up, so that we shall +have a clear field. Besides, we must be prompt, for this marriage +may mean a complete change in her life and habits. I must wire to +the King without delay." + +We had reached Baker Street and had stopped at the door. He was +searching his pockets for the key when someone passing said: + +"Good-night, Mister Sherlock Holmes." + +There were several people on the pavement at the time, but the +greeting appeared to come from a slim youth in an ulster who had +hurried by. + +"I've heard that voice before," said Holmes, staring down the +dimly lit street. "Now, I wonder who the deuce that could have +been." + + +III. + +I slept at Baker Street that night, and we were engaged upon our +toast and coffee in the morning when the King of Bohemia rushed +into the room. + +"You have really got it!" he cried, grasping Sherlock Holmes by +either shoulder and looking eagerly into his face. + +"Not yet." + +"But you have hopes?" + +"I have hopes." + +"Then, come. I am all impatience to be gone." + +"We must have a cab." + +"No, my brougham is waiting." + +"Then that will simplify matters." We descended and started off +once more for Briony Lodge. + +"Irene Adler is married," remarked Holmes. + +"Married! When?" + +"Yesterday." + +"But to whom?" + +"To an English lawyer named Norton." + +"But she could not love him." + +"I am in hopes that she does." + +"And why in hopes?" + +"Because it would spare your Majesty all fear of future +annoyance. If the lady loves her husband, she does not love your +Majesty. If she does not love your Majesty, there is no reason +why she should interfere with your Majesty's plan." + +"It is true. And yet--Well! I wish she had been of my own +station! What a queen she would have made!" He relapsed into a +moody silence, which was not broken until we drew up in +Serpentine Avenue. + +The door of Briony Lodge was open, and an elderly woman stood +upon the steps. She watched us with a sardonic eye as we stepped +from the brougham. + +"Mr. Sherlock Holmes, I believe?" said she. + +"I am Mr. Holmes," answered my companion, looking at her with a +questioning and rather startled gaze. + +"Indeed! My mistress told me that you were likely to call. She +left this morning with her husband by the 5:15 train from Charing +Cross for the Continent." + +"What!" Sherlock Holmes staggered back, white with chagrin and +surprise. "Do you mean that she has left England?" + +"Never to return." + +"And the papers?" asked the King hoarsely. "All is lost." + +"We shall see." He pushed past the servant and rushed into the +drawing-room, followed by the King and myself. The furniture was +scattered about in every direction, with dismantled shelves and +open drawers, as if the lady had hurriedly ransacked them before +her flight. Holmes rushed at the bell-pull, tore back a small +sliding shutter, and, plunging in his hand, pulled out a +photograph and a letter. The photograph was of Irene Adler +herself in evening dress, the letter was superscribed to +"Sherlock Holmes, Esq. To be left till called for." My friend +tore it open and we all three read it together. It was dated at +midnight of the preceding night and ran in this way: + +"MY DEAR MR. SHERLOCK HOLMES,--You really did it very well. You +took me in completely. Until after the alarm of fire, I had not a +suspicion. But then, when I found how I had betrayed myself, I +began to think. I had been warned against you months ago. I had +been told that if the King employed an agent it would certainly +be you. And your address had been given me. Yet, with all this, +you made me reveal what you wanted to know. Even after I became +suspicious, I found it hard to think evil of such a dear, kind +old clergyman. But, you know, I have been trained as an actress +myself. Male costume is nothing new to me. I often take advantage +of the freedom which it gives. I sent John, the coachman, to +watch you, ran up stairs, got into my walking-clothes, as I call +them, and came down just as you departed. + +"Well, I followed you to your door, and so made sure that I was +really an object of interest to the celebrated Mr. Sherlock +Holmes. Then I, rather imprudently, wished you good-night, and +started for the Temple to see my husband. + +"We both thought the best resource was flight, when pursued by +so formidable an antagonist; so you will find the nest empty when +you call to-morrow. As to the photograph, your client may rest in +peace. I love and am loved by a better man than he. The King may +do what he will without hindrance from one whom he has cruelly +wronged. I keep it only to safeguard myself, and to preserve a +weapon which will always secure me from any steps which he might +take in the future. I leave a photograph which he might care to +possess; and I remain, dear Mr. Sherlock Holmes, + + "Very truly yours, + "IRENE NORTON, née ADLER." + +"What a woman--oh, what a woman!" cried the King of Bohemia, when +we had all three read this epistle. "Did I not tell you how quick +and resolute she was? Would she not have made an admirable queen? +Is it not a pity that she was not on my level?" + +"From what I have seen of the lady she seems indeed to be on a +very different level to your Majesty," said Holmes coldly. "I am +sorry that I have not been able to bring your Majesty's business +to a more successful conclusion." + +"On the contrary, my dear sir," cried the King; "nothing could be +more successful. I know that her word is inviolate. The +photograph is now as safe as if it were in the fire." + +"I am glad to hear your Majesty say so." + +"I am immensely indebted to you. Pray tell me in what way I can +reward you. This ring--" He slipped an emerald snake ring from +his finger and held it out upon the palm of his hand. + +"Your Majesty has something which I should value even more +highly," said Holmes. + +"You have but to name it." + +"This photograph!" + +The King stared at him in amazement. + +"Irene's photograph!" he cried. "Certainly, if you wish it." + +"I thank your Majesty. Then there is no more to be done in the +matter. I have the honour to wish you a very good-morning." He +bowed, and, turning away without observing the hand which the +King had stretched out to him, he set off in my company for his +chambers. + +And that was how a great scandal threatened to affect the kingdom +of Bohemia, and how the best plans of Mr. Sherlock Holmes were +beaten by a woman's wit. He used to make merry over the +cleverness of women, but I have not heard him do it of late. And +when he speaks of Irene Adler, or when he refers to her +photograph, it is always under the honourable title of the woman. + + + +ADVENTURE II. THE RED-HEADED LEAGUE + +I had called upon my friend, Mr. Sherlock Holmes, one day in the +autumn of last year and found him in deep conversation with a +very stout, florid-faced, elderly gentleman with fiery red hair. +With an apology for my intrusion, I was about to withdraw when +Holmes pulled me abruptly into the room and closed the door +behind me. + +"You could not possibly have come at a better time, my dear +Watson," he said cordially. + +"I was afraid that you were engaged." + +"So I am. Very much so." + +"Then I can wait in the next room." + +"Not at all. This gentleman, Mr. Wilson, has been my partner and +helper in many of my most successful cases, and I have no +doubt that he will be of the utmost use to me in yours also." + +The stout gentleman half rose from his chair and gave a bob of +greeting, with a quick little questioning glance from his small +fat-encircled eyes. + +"Try the settee," said Holmes, relapsing into his armchair and +putting his fingertips together, as was his custom when in +judicial moods. "I know, my dear Watson, that you share my love +of all that is bizarre and outside the conventions and humdrum +routine of everyday life. You have shown your relish for it by +the enthusiasm which has prompted you to chronicle, and, if you +will excuse my saying so, somewhat to embellish so many of my own +little adventures." + +"Your cases have indeed been of the greatest interest to me," I +observed. + +"You will remember that I remarked the other day, just before we +went into the very simple problem presented by Miss Mary +Sutherland, that for strange effects and extraordinary +combinations we must go to life itself, which is always far more +daring than any effort of the imagination." + +"A proposition which I took the liberty of doubting." + +"You did, Doctor, but none the less you must come round to my +view, for otherwise I shall keep on piling fact upon fact on you +until your reason breaks down under them and acknowledges me to +be right. Now, Mr. Jabez Wilson here has been good enough to call +upon me this morning, and to begin a narrative which promises to +be one of the most singular which I have listened to for some +time. You have heard me remark that the strangest and most unique +things are very often connected not with the larger but with the +smaller crimes, and occasionally, indeed, where there is room for +doubt whether any positive crime has been committed. As far as I +have heard it is impossible for me to say whether the present +case is an instance of crime or not, but the course of events is +certainly among the most singular that I have ever listened to. +Perhaps, Mr. Wilson, you would have the great kindness to +recommence your narrative. I ask you not merely because my friend +Dr. Watson has not heard the opening part but also because the +peculiar nature of the story makes me anxious to have every +possible detail from your lips. As a rule, when I have heard some +slight indication of the course of events, I am able to guide +myself by the thousands of other similar cases which occur to my +memory. In the present instance I am forced to admit that the +facts are, to the best of my belief, unique." + +The portly client puffed out his chest with an appearance of some +little pride and pulled a dirty and wrinkled newspaper from the +inside pocket of his greatcoat. As he glanced down the +advertisement column, with his head thrust forward and the paper +flattened out upon his knee, I took a good look at the man and +endeavoured, after the fashion of my companion, to read the +indications which might be presented by his dress or appearance. + +I did not gain very much, however, by my inspection. Our visitor +bore every mark of being an average commonplace British +tradesman, obese, pompous, and slow. He wore rather baggy grey +shepherd's check trousers, a not over-clean black frock-coat, +unbuttoned in the front, and a drab waistcoat with a heavy brassy +Albert chain, and a square pierced bit of metal dangling down as +an ornament. A frayed top-hat and a faded brown overcoat with a +wrinkled velvet collar lay upon a chair beside him. Altogether, +look as I would, there was nothing remarkable about the man save +his blazing red head, and the expression of extreme chagrin and +discontent upon his features. + +Sherlock Holmes' quick eye took in my occupation, and he shook +his head with a smile as he noticed my questioning glances. +"Beyond the obvious facts that he has at some time done manual +labour, that he takes snuff, that he is a Freemason, that he has +been in China, and that he has done a considerable amount of +writing lately, I can deduce nothing else." + +Mr. Jabez Wilson started up in his chair, with his forefinger +upon the paper, but his eyes upon my companion. + +"How, in the name of good-fortune, did you know all that, Mr. +Holmes?" he asked. "How did you know, for example, that I did +manual labour. It's as true as gospel, for I began as a ship's +carpenter." + +"Your hands, my dear sir. Your right hand is quite a size larger +than your left. You have worked with it, and the muscles are more +developed." + +"Well, the snuff, then, and the Freemasonry?" + +"I won't insult your intelligence by telling you how I read that, +especially as, rather against the strict rules of your order, you +use an arc-and-compass breastpin." + +"Ah, of course, I forgot that. But the writing?" + +"What else can be indicated by that right cuff so very shiny for +five inches, and the left one with the smooth patch near the +elbow where you rest it upon the desk?" + +"Well, but China?" + +"The fish that you have tattooed immediately above your right +wrist could only have been done in China. I have made a small +study of tattoo marks and have even contributed to the literature +of the subject. That trick of staining the fishes' scales of a +delicate pink is quite peculiar to China. When, in addition, I +see a Chinese coin hanging from your watch-chain, the matter +becomes even more simple." + +Mr. Jabez Wilson laughed heavily. "Well, I never!" said he. "I +thought at first that you had done something clever, but I see +that there was nothing in it, after all." + +"I begin to think, Watson," said Holmes, "that I make a mistake +in explaining. 'Omne ignotum pro magnifico,' you know, and my +poor little reputation, such as it is, will suffer shipwreck if I +am so candid. Can you not find the advertisement, Mr. Wilson?" + +"Yes, I have got it now," he answered with his thick red finger +planted halfway down the column. "Here it is. This is what began +it all. You just read it for yourself, sir." + +I took the paper from him and read as follows: + +"TO THE RED-HEADED LEAGUE: On account of the bequest of the late +Ezekiah Hopkins, of Lebanon, Pennsylvania, U. S. A., there is now +another vacancy open which entitles a member of the League to a +salary of 4 pounds a week for purely nominal services. All +red-headed men who are sound in body and mind and above the age +of twenty-one years, are eligible. Apply in person on Monday, at +eleven o'clock, to Duncan Ross, at the offices of the League, 7 +Pope's Court, Fleet Street." + +"What on earth does this mean?" I ejaculated after I had twice +read over the extraordinary announcement. + +Holmes chuckled and wriggled in his chair, as was his habit when +in high spirits. "It is a little off the beaten track, isn't it?" +said he. "And now, Mr. Wilson, off you go at scratch and tell us +all about yourself, your household, and the effect which this +advertisement had upon your fortunes. You will first make a note, +Doctor, of the paper and the date." + +"It is The Morning Chronicle of April 27, 1890. Just two months +ago." + +"Very good. Now, Mr. Wilson?" + +"Well, it is just as I have been telling you, Mr. Sherlock +Holmes," said Jabez Wilson, mopping his forehead; "I have a small +pawnbroker's business at Coburg Square, near the City. It's not a +very large affair, and of late years it has not done more than +just give me a living. I used to be able to keep two assistants, +but now I only keep one; and I would have a job to pay him but +that he is willing to come for half wages so as to learn the +business." + +"What is the name of this obliging youth?" asked Sherlock Holmes. + +"His name is Vincent Spaulding, and he's not such a youth, +either. It's hard to say his age. I should not wish a smarter +assistant, Mr. Holmes; and I know very well that he could better +himself and earn twice what I am able to give him. But, after +all, if he is satisfied, why should I put ideas in his head?" + +"Why, indeed? You seem most fortunate in having an employé who +comes under the full market price. It is not a common experience +among employers in this age. I don't know that your assistant is +not as remarkable as your advertisement." + +"Oh, he has his faults, too," said Mr. Wilson. "Never was such a +fellow for photography. Snapping away with a camera when he ought +to be improving his mind, and then diving down into the cellar +like a rabbit into its hole to develop his pictures. That is his +main fault, but on the whole he's a good worker. There's no vice +in him." + +"He is still with you, I presume?" + +"Yes, sir. He and a girl of fourteen, who does a bit of simple +cooking and keeps the place clean--that's all I have in the +house, for I am a widower and never had any family. We live very +quietly, sir, the three of us; and we keep a roof over our heads +and pay our debts, if we do nothing more. + +"The first thing that put us out was that advertisement. +Spaulding, he came down into the office just this day eight +weeks, with this very paper in his hand, and he says: + +"'I wish to the Lord, Mr. Wilson, that I was a red-headed man.' + +"'Why that?' I asks. + +"'Why,' says he, 'here's another vacancy on the League of the +Red-headed Men. It's worth quite a little fortune to any man who +gets it, and I understand that there are more vacancies than +there are men, so that the trustees are at their wits' end what +to do with the money. If my hair would only change colour, here's +a nice little crib all ready for me to step into.' + +"'Why, what is it, then?' I asked. You see, Mr. Holmes, I am a +very stay-at-home man, and as my business came to me instead of +my having to go to it, I was often weeks on end without putting +my foot over the door-mat. In that way I didn't know much of what +was going on outside, and I was always glad of a bit of news. + +"'Have you never heard of the League of the Red-headed Men?' he +asked with his eyes open. + +"'Never.' + +"'Why, I wonder at that, for you are eligible yourself for one +of the vacancies.' + +"'And what are they worth?' I asked. + +"'Oh, merely a couple of hundred a year, but the work is slight, +and it need not interfere very much with one's other +occupations.' + +"Well, you can easily think that that made me prick up my ears, +for the business has not been over-good for some years, and an +extra couple of hundred would have been very handy. + +"'Tell me all about it,' said I. + +"'Well,' said he, showing me the advertisement, 'you can see for +yourself that the League has a vacancy, and there is the address +where you should apply for particulars. As far as I can make out, +the League was founded by an American millionaire, Ezekiah +Hopkins, who was very peculiar in his ways. He was himself +red-headed, and he had a great sympathy for all red-headed men; +so when he died it was found that he had left his enormous +fortune in the hands of trustees, with instructions to apply the +interest to the providing of easy berths to men whose hair is of +that colour. From all I hear it is splendid pay and very little to +do.' + +"'But,' said I, 'there would be millions of red-headed men who +would apply.' + +"'Not so many as you might think,' he answered. 'You see it is +really confined to Londoners, and to grown men. This American had +started from London when he was young, and he wanted to do the +old town a good turn. Then, again, I have heard it is no use your +applying if your hair is light red, or dark red, or anything but +real bright, blazing, fiery red. Now, if you cared to apply, Mr. +Wilson, you would just walk in; but perhaps it would hardly be +worth your while to put yourself out of the way for the sake of a +few hundred pounds.' + +"Now, it is a fact, gentlemen, as you may see for yourselves, +that my hair is of a very full and rich tint, so that it seemed +to me that if there was to be any competition in the matter I +stood as good a chance as any man that I had ever met. Vincent +Spaulding seemed to know so much about it that I thought he might +prove useful, so I just ordered him to put up the shutters for +the day and to come right away with me. He was very willing to +have a holiday, so we shut the business up and started off for +the address that was given us in the advertisement. + +"I never hope to see such a sight as that again, Mr. Holmes. From +north, south, east, and west every man who had a shade of red in +his hair had tramped into the city to answer the advertisement. +Fleet Street was choked with red-headed folk, and Pope's Court +looked like a coster's orange barrow. I should not have thought +there were so many in the whole country as were brought together +by that single advertisement. Every shade of colour they +were--straw, lemon, orange, brick, Irish-setter, liver, clay; +but, as Spaulding said, there were not many who had the real +vivid flame-coloured tint. When I saw how many were waiting, I +would have given it up in despair; but Spaulding would not hear +of it. How he did it I could not imagine, but he pushed and +pulled and butted until he got me through the crowd, and right up +to the steps which led to the office. There was a double stream +upon the stair, some going up in hope, and some coming back +dejected; but we wedged in as well as we could and soon found +ourselves in the office." + +"Your experience has been a most entertaining one," remarked +Holmes as his client paused and refreshed his memory with a huge +pinch of snuff. "Pray continue your very interesting statement." + +"There was nothing in the office but a couple of wooden chairs +and a deal table, behind which sat a small man with a head that +was even redder than mine. He said a few words to each candidate +as he came up, and then he always managed to find some fault in +them which would disqualify them. Getting a vacancy did not seem +to be such a very easy matter, after all. However, when our turn +came the little man was much more favourable to me than to any of +the others, and he closed the door as we entered, so that he +might have a private word with us. + +"'This is Mr. Jabez Wilson,' said my assistant, 'and he is +willing to fill a vacancy in the League.' + +"'And he is admirably suited for it,' the other answered. 'He has +every requirement. I cannot recall when I have seen anything so +fine.' He took a step backward, cocked his head on one side, and +gazed at my hair until I felt quite bashful. Then suddenly he +plunged forward, wrung my hand, and congratulated me warmly on my +success. + +"'It would be injustice to hesitate,' said he. 'You will, +however, I am sure, excuse me for taking an obvious precaution.' +With that he seized my hair in both his hands, and tugged until I +yelled with the pain. 'There is water in your eyes,' said he as +he released me. 'I perceive that all is as it should be. But we +have to be careful, for we have twice been deceived by wigs and +once by paint. I could tell you tales of cobbler's wax which +would disgust you with human nature.' He stepped over to the +window and shouted through it at the top of his voice that the +vacancy was filled. A groan of disappointment came up from below, +and the folk all trooped away in different directions until there +was not a red-head to be seen except my own and that of the +manager. + +"'My name,' said he, 'is Mr. Duncan Ross, and I am myself one of +the pensioners upon the fund left by our noble benefactor. Are +you a married man, Mr. Wilson? Have you a family?' + +"I answered that I had not. + +"His face fell immediately. + +"'Dear me!' he said gravely, 'that is very serious indeed! I am +sorry to hear you say that. The fund was, of course, for the +propagation and spread of the red-heads as well as for their +maintenance. It is exceedingly unfortunate that you should be a +bachelor.' + +"My face lengthened at this, Mr. Holmes, for I thought that I was +not to have the vacancy after all; but after thinking it over for +a few minutes he said that it would be all right. + +"'In the case of another,' said he, 'the objection might be +fatal, but we must stretch a point in favour of a man with such a +head of hair as yours. When shall you be able to enter upon your +new duties?' + +"'Well, it is a little awkward, for I have a business already,' +said I. + +"'Oh, never mind about that, Mr. Wilson!' said Vincent Spaulding. +'I should be able to look after that for you.' + +"'What would be the hours?' I asked. + +"'Ten to two.' + +"Now a pawnbroker's business is mostly done of an evening, Mr. +Holmes, especially Thursday and Friday evening, which is just +before pay-day; so it would suit me very well to earn a little in +the mornings. Besides, I knew that my assistant was a good man, +and that he would see to anything that turned up. + +"'That would suit me very well,' said I. 'And the pay?' + +"'Is 4 pounds a week.' + +"'And the work?' + +"'Is purely nominal.' + +"'What do you call purely nominal?' + +"'Well, you have to be in the office, or at least in the +building, the whole time. If you leave, you forfeit your whole +position forever. The will is very clear upon that point. You +don't comply with the conditions if you budge from the office +during that time.' + +"'It's only four hours a day, and I should not think of leaving,' +said I. + +"'No excuse will avail,' said Mr. Duncan Ross; 'neither sickness +nor business nor anything else. There you must stay, or you lose +your billet.' + +"'And the work?' + +"'Is to copy out the "Encyclopaedia Britannica." There is the first +volume of it in that press. You must find your own ink, pens, and +blotting-paper, but we provide this table and chair. Will you be +ready to-morrow?' + +"'Certainly,' I answered. + +"'Then, good-bye, Mr. Jabez Wilson, and let me congratulate you +once more on the important position which you have been fortunate +enough to gain.' He bowed me out of the room and I went home with +my assistant, hardly knowing what to say or do, I was so pleased +at my own good fortune. + +"Well, I thought over the matter all day, and by evening I was in +low spirits again; for I had quite persuaded myself that the +whole affair must be some great hoax or fraud, though what its +object might be I could not imagine. It seemed altogether past +belief that anyone could make such a will, or that they would pay +such a sum for doing anything so simple as copying out the +'Encyclopaedia Britannica.' Vincent Spaulding did what he could to +cheer me up, but by bedtime I had reasoned myself out of the +whole thing. However, in the morning I determined to have a look +at it anyhow, so I bought a penny bottle of ink, and with a +quill-pen, and seven sheets of foolscap paper, I started off for +Pope's Court. + +"Well, to my surprise and delight, everything was as right as +possible. The table was set out ready for me, and Mr. Duncan Ross +was there to see that I got fairly to work. He started me off +upon the letter A, and then he left me; but he would drop in from +time to time to see that all was right with me. At two o'clock he +bade me good-day, complimented me upon the amount that I had +written, and locked the door of the office after me. + +"This went on day after day, Mr. Holmes, and on Saturday the +manager came in and planked down four golden sovereigns for my +week's work. It was the same next week, and the same the week +after. Every morning I was there at ten, and every afternoon I +left at two. By degrees Mr. Duncan Ross took to coming in only +once of a morning, and then, after a time, he did not come in at +all. Still, of course, I never dared to leave the room for an +instant, for I was not sure when he might come, and the billet +was such a good one, and suited me so well, that I would not risk +the loss of it. + +"Eight weeks passed away like this, and I had written about +Abbots and Archery and Armour and Architecture and Attica, and +hoped with diligence that I might get on to the B's before very +long. It cost me something in foolscap, and I had pretty nearly +filled a shelf with my writings. And then suddenly the whole +business came to an end." + +"To an end?" + +"Yes, sir. And no later than this morning. I went to my work as +usual at ten o'clock, but the door was shut and locked, with a +little square of cardboard hammered on to the middle of the +panel with a tack. Here it is, and you can read for yourself." + +He held up a piece of white cardboard about the size of a sheet +of note-paper. It read in this fashion: + + THE RED-HEADED LEAGUE + + IS + + DISSOLVED. + + October 9, 1890. + +Sherlock Holmes and I surveyed this curt announcement and the +rueful face behind it, until the comical side of the affair so +completely overtopped every other consideration that we both +burst out into a roar of laughter. + +"I cannot see that there is anything very funny," cried our +client, flushing up to the roots of his flaming head. "If you can +do nothing better than laugh at me, I can go elsewhere." + +"No, no," cried Holmes, shoving him back into the chair from +which he had half risen. "I really wouldn't miss your case for +the world. It is most refreshingly unusual. But there is, if you +will excuse my saying so, something just a little funny about it. +Pray what steps did you take when you found the card upon the +door?" + +"I was staggered, sir. I did not know what to do. Then I called +at the offices round, but none of them seemed to know anything +about it. Finally, I went to the landlord, who is an accountant +living on the ground-floor, and I asked him if he could tell me +what had become of the Red-headed League. He said that he had +never heard of any such body. Then I asked him who Mr. Duncan +Ross was. He answered that the name was new to him. + +"'Well,' said I, 'the gentleman at No. 4.' + +"'What, the red-headed man?' + +"'Yes.' + +"'Oh,' said he, 'his name was William Morris. He was a solicitor +and was using my room as a temporary convenience until his new +premises were ready. He moved out yesterday.' + +"'Where could I find him?' + +"'Oh, at his new offices. He did tell me the address. Yes, 17 +King Edward Street, near St. Paul's.' + +"I started off, Mr. Holmes, but when I got to that address it was +a manufactory of artificial knee-caps, and no one in it had ever +heard of either Mr. William Morris or Mr. Duncan Ross." + +"And what did you do then?" asked Holmes. + +"I went home to Saxe-Coburg Square, and I took the advice of my +assistant. But he could not help me in any way. He could only say +that if I waited I should hear by post. But that was not quite +good enough, Mr. Holmes. I did not wish to lose such a place +without a struggle, so, as I had heard that you were good enough +to give advice to poor folk who were in need of it, I came right +away to you." + +"And you did very wisely," said Holmes. "Your case is an +exceedingly remarkable one, and I shall be happy to look into it. +From what you have told me I think that it is possible that +graver issues hang from it than might at first sight appear." + +"Grave enough!" said Mr. Jabez Wilson. "Why, I have lost four +pound a week." + +"As far as you are personally concerned," remarked Holmes, "I do +not see that you have any grievance against this extraordinary +league. On the contrary, you are, as I understand, richer by some +30 pounds, to say nothing of the minute knowledge which you have +gained on every subject which comes under the letter A. You have +lost nothing by them." + +"No, sir. But I want to find out about them, and who they are, +and what their object was in playing this prank--if it was a +prank--upon me. It was a pretty expensive joke for them, for it +cost them two and thirty pounds." + +"We shall endeavour to clear up these points for you. And, first, +one or two questions, Mr. Wilson. This assistant of yours who +first called your attention to the advertisement--how long had he +been with you?" + +"About a month then." + +"How did he come?" + +"In answer to an advertisement." + +"Was he the only applicant?" + +"No, I had a dozen." + +"Why did you pick him?" + +"Because he was handy and would come cheap." + +"At half-wages, in fact." + +"Yes." + +"What is he like, this Vincent Spaulding?" + +"Small, stout-built, very quick in his ways, no hair on his face, +though he's not short of thirty. Has a white splash of acid upon +his forehead." + +Holmes sat up in his chair in considerable excitement. "I thought +as much," said he. "Have you ever observed that his ears are +pierced for earrings?" + +"Yes, sir. He told me that a gipsy had done it for him when he +was a lad." + +"Hum!" said Holmes, sinking back in deep thought. "He is still +with you?" + +"Oh, yes, sir; I have only just left him." + +"And has your business been attended to in your absence?" + +"Nothing to complain of, sir. There's never very much to do of a +morning." + +"That will do, Mr. Wilson. I shall be happy to give you an +opinion upon the subject in the course of a day or two. To-day is +Saturday, and I hope that by Monday we may come to a conclusion." + +"Well, Watson," said Holmes when our visitor had left us, "what +do you make of it all?" + +"I make nothing of it," I answered frankly. "It is a most +mysterious business." + +"As a rule," said Holmes, "the more bizarre a thing is the less +mysterious it proves to be. It is your commonplace, featureless +crimes which are really puzzling, just as a commonplace face is +the most difficult to identify. But I must be prompt over this +matter." + +"What are you going to do, then?" I asked. + +"To smoke," he answered. "It is quite a three pipe problem, and I +beg that you won't speak to me for fifty minutes." He curled +himself up in his chair, with his thin knees drawn up to his +hawk-like nose, and there he sat with his eyes closed and his +black clay pipe thrusting out like the bill of some strange bird. +I had come to the conclusion that he had dropped asleep, and +indeed was nodding myself, when he suddenly sprang out of his +chair with the gesture of a man who has made up his mind and put +his pipe down upon the mantelpiece. + +"Sarasate plays at the St. James's Hall this afternoon," he +remarked. "What do you think, Watson? Could your patients spare +you for a few hours?" + +"I have nothing to do to-day. My practice is never very +absorbing." + +"Then put on your hat and come. I am going through the City +first, and we can have some lunch on the way. I observe that +there is a good deal of German music on the programme, which is +rather more to my taste than Italian or French. It is +introspective, and I want to introspect. Come along!" + +We travelled by the Underground as far as Aldersgate; and a short +walk took us to Saxe-Coburg Square, the scene of the singular +story which we had listened to in the morning. It was a poky, +little, shabby-genteel place, where four lines of dingy +two-storied brick houses looked out into a small railed-in +enclosure, where a lawn of weedy grass and a few clumps of faded +laurel-bushes made a hard fight against a smoke-laden and +uncongenial atmosphere. Three gilt balls and a brown board with +"JABEZ WILSON" in white letters, upon a corner house, announced +the place where our red-headed client carried on his business. +Sherlock Holmes stopped in front of it with his head on one side +and looked it all over, with his eyes shining brightly between +puckered lids. Then he walked slowly up the street, and then down +again to the corner, still looking keenly at the houses. Finally +he returned to the pawnbroker's, and, having thumped vigorously +upon the pavement with his stick two or three times, he went up +to the door and knocked. It was instantly opened by a +bright-looking, clean-shaven young fellow, who asked him to step +in. + +"Thank you," said Holmes, "I only wished to ask you how you would +go from here to the Strand." + +"Third right, fourth left," answered the assistant promptly, +closing the door. + +"Smart fellow, that," observed Holmes as we walked away. "He is, +in my judgment, the fourth smartest man in London, and for daring +I am not sure that he has not a claim to be third. I have known +something of him before." + +"Evidently," said I, "Mr. Wilson's assistant counts for a good +deal in this mystery of the Red-headed League. I am sure that you +inquired your way merely in order that you might see him." + +"Not him." + +"What then?" + +"The knees of his trousers." + +"And what did you see?" + +"What I expected to see." + +"Why did you beat the pavement?" + +"My dear doctor, this is a time for observation, not for talk. We +are spies in an enemy's country. We know something of Saxe-Coburg +Square. Let us now explore the parts which lie behind it." + +The road in which we found ourselves as we turned round the +corner from the retired Saxe-Coburg Square presented as great a +contrast to it as the front of a picture does to the back. It was +one of the main arteries which conveyed the traffic of the City +to the north and west. The roadway was blocked with the immense +stream of commerce flowing in a double tide inward and outward, +while the footpaths were black with the hurrying swarm of +pedestrians. It was difficult to realise as we looked at the line +of fine shops and stately business premises that they really +abutted on the other side upon the faded and stagnant square +which we had just quitted. + +"Let me see," said Holmes, standing at the corner and glancing +along the line, "I should like just to remember the order of the +houses here. It is a hobby of mine to have an exact knowledge of +London. There is Mortimer's, the tobacconist, the little +newspaper shop, the Coburg branch of the City and Suburban Bank, +the Vegetarian Restaurant, and McFarlane's carriage-building +depot. That carries us right on to the other block. And now, +Doctor, we've done our work, so it's time we had some play. A +sandwich and a cup of coffee, and then off to violin-land, where +all is sweetness and delicacy and harmony, and there are no +red-headed clients to vex us with their conundrums." + +My friend was an enthusiastic musician, being himself not only a +very capable performer but a composer of no ordinary merit. All +the afternoon he sat in the stalls wrapped in the most perfect +happiness, gently waving his long, thin fingers in time to the +music, while his gently smiling face and his languid, dreamy eyes +were as unlike those of Holmes the sleuth-hound, Holmes the +relentless, keen-witted, ready-handed criminal agent, as it was +possible to conceive. In his singular character the dual nature +alternately asserted itself, and his extreme exactness and +astuteness represented, as I have often thought, the reaction +against the poetic and contemplative mood which occasionally +predominated in him. The swing of his nature took him from +extreme languor to devouring energy; and, as I knew well, he was +never so truly formidable as when, for days on end, he had been +lounging in his armchair amid his improvisations and his +black-letter editions. Then it was that the lust of the chase +would suddenly come upon him, and that his brilliant reasoning +power would rise to the level of intuition, until those who were +unacquainted with his methods would look askance at him as on a +man whose knowledge was not that of other mortals. When I saw him +that afternoon so enwrapped in the music at St. James's Hall I +felt that an evil time might be coming upon those whom he had set +himself to hunt down. + +"You want to go home, no doubt, Doctor," he remarked as we +emerged. + +"Yes, it would be as well." + +"And I have some business to do which will take some hours. This +business at Coburg Square is serious." + +"Why serious?" + +"A considerable crime is in contemplation. I have every reason to +believe that we shall be in time to stop it. But to-day being +Saturday rather complicates matters. I shall want your help +to-night." + +"At what time?" + +"Ten will be early enough." + +"I shall be at Baker Street at ten." + +"Very well. And, I say, Doctor, there may be some little danger, +so kindly put your army revolver in your pocket." He waved his +hand, turned on his heel, and disappeared in an instant among the +crowd. + +I trust that I am not more dense than my neighbours, but I was +always oppressed with a sense of my own stupidity in my dealings +with Sherlock Holmes. Here I had heard what he had heard, I had +seen what he had seen, and yet from his words it was evident that +he saw clearly not only what had happened but what was about to +happen, while to me the whole business was still confused and +grotesque. As I drove home to my house in Kensington I thought +over it all, from the extraordinary story of the red-headed +copier of the "Encyclopaedia" down to the visit to Saxe-Coburg +Square, and the ominous words with which he had parted from me. +What was this nocturnal expedition, and why should I go armed? +Where were we going, and what were we to do? I had the hint from +Holmes that this smooth-faced pawnbroker's assistant was a +formidable man--a man who might play a deep game. I tried to +puzzle it out, but gave it up in despair and set the matter aside +until night should bring an explanation. + +It was a quarter-past nine when I started from home and made my +way across the Park, and so through Oxford Street to Baker +Street. Two hansoms were standing at the door, and as I entered +the passage I heard the sound of voices from above. On entering +his room I found Holmes in animated conversation with two men, +one of whom I recognised as Peter Jones, the official police +agent, while the other was a long, thin, sad-faced man, with a +very shiny hat and oppressively respectable frock-coat. + +"Ha! Our party is complete," said Holmes, buttoning up his +pea-jacket and taking his heavy hunting crop from the rack. +"Watson, I think you know Mr. Jones, of Scotland Yard? Let me +introduce you to Mr. Merryweather, who is to be our companion in +to-night's adventure." + +"We're hunting in couples again, Doctor, you see," said Jones in +his consequential way. "Our friend here is a wonderful man for +starting a chase. All he wants is an old dog to help him to do +the running down." + +"I hope a wild goose may not prove to be the end of our chase," +observed Mr. Merryweather gloomily. + +"You may place considerable confidence in Mr. Holmes, sir," said +the police agent loftily. "He has his own little methods, which +are, if he won't mind my saying so, just a little too theoretical +and fantastic, but he has the makings of a detective in him. It +is not too much to say that once or twice, as in that business of +the Sholto murder and the Agra treasure, he has been more nearly +correct than the official force." + +"Oh, if you say so, Mr. Jones, it is all right," said the +stranger with deference. "Still, I confess that I miss my rubber. +It is the first Saturday night for seven-and-twenty years that I +have not had my rubber." + +"I think you will find," said Sherlock Holmes, "that you will +play for a higher stake to-night than you have ever done yet, and +that the play will be more exciting. For you, Mr. Merryweather, +the stake will be some 30,000 pounds; and for you, Jones, it will +be the man upon whom you wish to lay your hands." + +"John Clay, the murderer, thief, smasher, and forger. He's a +young man, Mr. Merryweather, but he is at the head of his +profession, and I would rather have my bracelets on him than on +any criminal in London. He's a remarkable man, is young John +Clay. His grandfather was a royal duke, and he himself has been +to Eton and Oxford. His brain is as cunning as his fingers, and +though we meet signs of him at every turn, we never know where to +find the man himself. He'll crack a crib in Scotland one week, +and be raising money to build an orphanage in Cornwall the next. +I've been on his track for years and have never set eyes on him +yet." + +"I hope that I may have the pleasure of introducing you to-night. +I've had one or two little turns also with Mr. John Clay, and I +agree with you that he is at the head of his profession. It is +past ten, however, and quite time that we started. If you two +will take the first hansom, Watson and I will follow in the +second." + +Sherlock Holmes was not very communicative during the long drive +and lay back in the cab humming the tunes which he had heard in +the afternoon. We rattled through an endless labyrinth of gas-lit +streets until we emerged into Farrington Street. + +"We are close there now," my friend remarked. "This fellow +Merryweather is a bank director, and personally interested in the +matter. I thought it as well to have Jones with us also. He is +not a bad fellow, though an absolute imbecile in his profession. +He has one positive virtue. He is as brave as a bulldog and as +tenacious as a lobster if he gets his claws upon anyone. Here we +are, and they are waiting for us." + +We had reached the same crowded thoroughfare in which we had +found ourselves in the morning. Our cabs were dismissed, and, +following the guidance of Mr. Merryweather, we passed down a +narrow passage and through a side door, which he opened for us. +Within there was a small corridor, which ended in a very massive +iron gate. This also was opened, and led down a flight of winding +stone steps, which terminated at another formidable gate. Mr. +Merryweather stopped to light a lantern, and then conducted us +down a dark, earth-smelling passage, and so, after opening a +third door, into a huge vault or cellar, which was piled all +round with crates and massive boxes. + +"You are not very vulnerable from above," Holmes remarked as he +held up the lantern and gazed about him. + +"Nor from below," said Mr. Merryweather, striking his stick upon +the flags which lined the floor. "Why, dear me, it sounds quite +hollow!" he remarked, looking up in surprise. + +"I must really ask you to be a little more quiet!" said Holmes +severely. "You have already imperilled the whole success of our +expedition. Might I beg that you would have the goodness to sit +down upon one of those boxes, and not to interfere?" + +The solemn Mr. Merryweather perched himself upon a crate, with a +very injured expression upon his face, while Holmes fell upon his +knees upon the floor and, with the lantern and a magnifying lens, +began to examine minutely the cracks between the stones. A few +seconds sufficed to satisfy him, for he sprang to his feet again +and put his glass in his pocket. + +"We have at least an hour before us," he remarked, "for they can +hardly take any steps until the good pawnbroker is safely in bed. +Then they will not lose a minute, for the sooner they do their +work the longer time they will have for their escape. We are at +present, Doctor--as no doubt you have divined--in the cellar of +the City branch of one of the principal London banks. Mr. +Merryweather is the chairman of directors, and he will explain to +you that there are reasons why the more daring criminals of +London should take a considerable interest in this cellar at +present." + +"It is our French gold," whispered the director. "We have had +several warnings that an attempt might be made upon it." + +"Your French gold?" + +"Yes. We had occasion some months ago to strengthen our resources +and borrowed for that purpose 30,000 napoleons from the Bank of +France. It has become known that we have never had occasion to +unpack the money, and that it is still lying in our cellar. The +crate upon which I sit contains 2,000 napoleons packed between +layers of lead foil. Our reserve of bullion is much larger at +present than is usually kept in a single branch office, and the +directors have had misgivings upon the subject." + +"Which were very well justified," observed Holmes. "And now it is +time that we arranged our little plans. I expect that within an +hour matters will come to a head. In the meantime Mr. +Merryweather, we must put the screen over that dark lantern." + +"And sit in the dark?" + +"I am afraid so. I had brought a pack of cards in my pocket, and +I thought that, as we were a partie carrée, you might have your +rubber after all. But I see that the enemy's preparations have +gone so far that we cannot risk the presence of a light. And, +first of all, we must choose our positions. These are daring men, +and though we shall take them at a disadvantage, they may do us +some harm unless we are careful. I shall stand behind this crate, +and do you conceal yourselves behind those. Then, when I flash a +light upon them, close in swiftly. If they fire, Watson, have no +compunction about shooting them down." + +I placed my revolver, cocked, upon the top of the wooden case +behind which I crouched. Holmes shot the slide across the front +of his lantern and left us in pitch darkness--such an absolute +darkness as I have never before experienced. The smell of hot +metal remained to assure us that the light was still there, ready +to flash out at a moment's notice. To me, with my nerves worked +up to a pitch of expectancy, there was something depressing and +subduing in the sudden gloom, and in the cold dank air of the +vault. + +"They have but one retreat," whispered Holmes. "That is back +through the house into Saxe-Coburg Square. I hope that you have +done what I asked you, Jones?" + +"I have an inspector and two officers waiting at the front door." + +"Then we have stopped all the holes. And now we must be silent +and wait." + +What a time it seemed! From comparing notes afterwards it was but +an hour and a quarter, yet it appeared to me that the night must +have almost gone and the dawn be breaking above us. My limbs +were weary and stiff, for I feared to change my position; yet my +nerves were worked up to the highest pitch of tension, and my +hearing was so acute that I could not only hear the gentle +breathing of my companions, but I could distinguish the deeper, +heavier in-breath of the bulky Jones from the thin, sighing note +of the bank director. From my position I could look over the case +in the direction of the floor. Suddenly my eyes caught the glint +of a light. + +At first it was but a lurid spark upon the stone pavement. Then +it lengthened out until it became a yellow line, and then, +without any warning or sound, a gash seemed to open and a hand +appeared, a white, almost womanly hand, which felt about in the +centre of the little area of light. For a minute or more the +hand, with its writhing fingers, protruded out of the floor. Then +it was withdrawn as suddenly as it appeared, and all was dark +again save the single lurid spark which marked a chink between +the stones. + +Its disappearance, however, was but momentary. With a rending, +tearing sound, one of the broad, white stones turned over upon +its side and left a square, gaping hole, through which streamed +the light of a lantern. Over the edge there peeped a clean-cut, +boyish face, which looked keenly about it, and then, with a hand +on either side of the aperture, drew itself shoulder-high and +waist-high, until one knee rested upon the edge. In another +instant he stood at the side of the hole and was hauling after +him a companion, lithe and small like himself, with a pale face +and a shock of very red hair. + +"It's all clear," he whispered. "Have you the chisel and the +bags? Great Scott! Jump, Archie, jump, and I'll swing for it!" + +Sherlock Holmes had sprung out and seized the intruder by the +collar. The other dived down the hole, and I heard the sound of +rending cloth as Jones clutched at his skirts. The light flashed +upon the barrel of a revolver, but Holmes' hunting crop came +down on the man's wrist, and the pistol clinked upon the stone +floor. + +"It's no use, John Clay," said Holmes blandly. "You have no +chance at all." + +"So I see," the other answered with the utmost coolness. "I fancy +that my pal is all right, though I see you have got his +coat-tails." + +"There are three men waiting for him at the door," said Holmes. + +"Oh, indeed! You seem to have done the thing very completely. I +must compliment you." + +"And I you," Holmes answered. "Your red-headed idea was very new +and effective." + +"You'll see your pal again presently," said Jones. "He's quicker +at climbing down holes than I am. Just hold out while I fix the +derbies." + +"I beg that you will not touch me with your filthy hands," +remarked our prisoner as the handcuffs clattered upon his wrists. +"You may not be aware that I have royal blood in my veins. Have +the goodness, also, when you address me always to say 'sir' and +'please.'" + +"All right," said Jones with a stare and a snigger. "Well, would +you please, sir, march upstairs, where we can get a cab to carry +your Highness to the police-station?" + +"That is better," said John Clay serenely. He made a sweeping bow +to the three of us and walked quietly off in the custody of the +detective. + +"Really, Mr. Holmes," said Mr. Merryweather as we followed them +from the cellar, "I do not know how the bank can thank you or +repay you. There is no doubt that you have detected and defeated +in the most complete manner one of the most determined attempts +at bank robbery that have ever come within my experience." + +"I have had one or two little scores of my own to settle with Mr. +John Clay," said Holmes. "I have been at some small expense over +this matter, which I shall expect the bank to refund, but beyond +that I am amply repaid by having had an experience which is in +many ways unique, and by hearing the very remarkable narrative of +the Red-headed League." + + +"You see, Watson," he explained in the early hours of the morning +as we sat over a glass of whisky and soda in Baker Street, "it +was perfectly obvious from the first that the only possible +object of this rather fantastic business of the advertisement of +the League, and the copying of the 'Encyclopaedia,' must be to get +this not over-bright pawnbroker out of the way for a number of +hours every day. It was a curious way of managing it, but, +really, it would be difficult to suggest a better. The method was +no doubt suggested to Clay's ingenious mind by the colour of his +accomplice's hair. The 4 pounds a week was a lure which must draw +him, and what was it to them, who were playing for thousands? +They put in the advertisement, one rogue has the temporary +office, the other rogue incites the man to apply for it, and +together they manage to secure his absence every morning in the +week. From the time that I heard of the assistant having come for +half wages, it was obvious to me that he had some strong motive +for securing the situation." + +"But how could you guess what the motive was?" + +"Had there been women in the house, I should have suspected a +mere vulgar intrigue. That, however, was out of the question. The +man's business was a small one, and there was nothing in his +house which could account for such elaborate preparations, and +such an expenditure as they were at. It must, then, be something +out of the house. What could it be? I thought of the assistant's +fondness for photography, and his trick of vanishing into the +cellar. The cellar! There was the end of this tangled clue. Then +I made inquiries as to this mysterious assistant and found that I +had to deal with one of the coolest and most daring criminals in +London. He was doing something in the cellar--something which +took many hours a day for months on end. What could it be, once +more? I could think of nothing save that he was running a tunnel +to some other building. + +"So far I had got when we went to visit the scene of action. I +surprised you by beating upon the pavement with my stick. I was +ascertaining whether the cellar stretched out in front or behind. +It was not in front. Then I rang the bell, and, as I hoped, the +assistant answered it. We have had some skirmishes, but we had +never set eyes upon each other before. I hardly looked at his +face. His knees were what I wished to see. You must yourself have +remarked how worn, wrinkled, and stained they were. They spoke of +those hours of burrowing. The only remaining point was what they +were burrowing for. I walked round the corner, saw the City and +Suburban Bank abutted on our friend's premises, and felt that I +had solved my problem. When you drove home after the concert I +called upon Scotland Yard and upon the chairman of the bank +directors, with the result that you have seen." + +"And how could you tell that they would make their attempt +to-night?" I asked. + +"Well, when they closed their League offices that was a sign that +they cared no longer about Mr. Jabez Wilson's presence--in other +words, that they had completed their tunnel. But it was essential +that they should use it soon, as it might be discovered, or the +bullion might be removed. Saturday would suit them better than +any other day, as it would give them two days for their escape. +For all these reasons I expected them to come to-night." + +"You reasoned it out beautifully," I exclaimed in unfeigned +admiration. "It is so long a chain, and yet every link rings +true." + +"It saved me from ennui," he answered, yawning. "Alas! I already +feel it closing in upon me. My life is spent in one long effort +to escape from the commonplaces of existence. These little +problems help me to do so." + +"And you are a benefactor of the race," said I. + +He shrugged his shoulders. "Well, perhaps, after all, it is of +some little use," he remarked. "'L'homme c'est rien--l'oeuvre +c'est tout,' as Gustave Flaubert wrote to George Sand." + + + +ADVENTURE III. A CASE OF IDENTITY + +"My dear fellow," said Sherlock Holmes as we sat on either side +of the fire in his lodgings at Baker Street, "life is infinitely +stranger than anything which the mind of man could invent. We +would not dare to conceive the things which are really mere +commonplaces of existence. If we could fly out of that window +hand in hand, hover over this great city, gently remove the +roofs, and peep in at the queer things which are going on, the +strange coincidences, the plannings, the cross-purposes, the +wonderful chains of events, working through generations, and +leading to the most outré results, it would make all fiction with +its conventionalities and foreseen conclusions most stale and +unprofitable." + +"And yet I am not convinced of it," I answered. "The cases which +come to light in the papers are, as a rule, bald enough, and +vulgar enough. We have in our police reports realism pushed to +its extreme limits, and yet the result is, it must be confessed, +neither fascinating nor artistic." + +"A certain selection and discretion must be used in producing a +realistic effect," remarked Holmes. "This is wanting in the +police report, where more stress is laid, perhaps, upon the +platitudes of the magistrate than upon the details, which to an +observer contain the vital essence of the whole matter. Depend +upon it, there is nothing so unnatural as the commonplace." + +I smiled and shook my head. "I can quite understand your thinking +so," I said. "Of course, in your position of unofficial adviser +and helper to everybody who is absolutely puzzled, throughout +three continents, you are brought in contact with all that is +strange and bizarre. But here"--I picked up the morning paper +from the ground--"let us put it to a practical test. Here is the +first heading upon which I come. 'A husband's cruelty to his +wife.' There is half a column of print, but I know without +reading it that it is all perfectly familiar to me. There is, of +course, the other woman, the drink, the push, the blow, the +bruise, the sympathetic sister or landlady. The crudest of +writers could invent nothing more crude." + +"Indeed, your example is an unfortunate one for your argument," +said Holmes, taking the paper and glancing his eye down it. "This +is the Dundas separation case, and, as it happens, I was engaged +in clearing up some small points in connection with it. The +husband was a teetotaler, there was no other woman, and the +conduct complained of was that he had drifted into the habit of +winding up every meal by taking out his false teeth and hurling +them at his wife, which, you will allow, is not an action likely +to occur to the imagination of the average story-teller. Take a +pinch of snuff, Doctor, and acknowledge that I have scored over +you in your example." + +He held out his snuffbox of old gold, with a great amethyst in +the centre of the lid. Its splendour was in such contrast to his +homely ways and simple life that I could not help commenting upon +it. + +"Ah," said he, "I forgot that I had not seen you for some weeks. +It is a little souvenir from the King of Bohemia in return for my +assistance in the case of the Irene Adler papers." + +"And the ring?" I asked, glancing at a remarkable brilliant which +sparkled upon his finger. + +"It was from the reigning family of Holland, though the matter in +which I served them was of such delicacy that I cannot confide it +even to you, who have been good enough to chronicle one or two of +my little problems." + +"And have you any on hand just now?" I asked with interest. + +"Some ten or twelve, but none which present any feature of +interest. They are important, you understand, without being +interesting. Indeed, I have found that it is usually in +unimportant matters that there is a field for the observation, +and for the quick analysis of cause and effect which gives the +charm to an investigation. The larger crimes are apt to be the +simpler, for the bigger the crime the more obvious, as a rule, is +the motive. In these cases, save for one rather intricate matter +which has been referred to me from Marseilles, there is nothing +which presents any features of interest. It is possible, however, +that I may have something better before very many minutes are +over, for this is one of my clients, or I am much mistaken." + +He had risen from his chair and was standing between the parted +blinds gazing down into the dull neutral-tinted London street. +Looking over his shoulder, I saw that on the pavement opposite +there stood a large woman with a heavy fur boa round her neck, +and a large curling red feather in a broad-brimmed hat which was +tilted in a coquettish Duchess of Devonshire fashion over her +ear. From under this great panoply she peeped up in a nervous, +hesitating fashion at our windows, while her body oscillated +backward and forward, and her fingers fidgeted with her glove +buttons. Suddenly, with a plunge, as of the swimmer who leaves +the bank, she hurried across the road, and we heard the sharp +clang of the bell. + +"I have seen those symptoms before," said Holmes, throwing his +cigarette into the fire. "Oscillation upon the pavement always +means an affaire de coeur. She would like advice, but is not sure +that the matter is not too delicate for communication. And yet +even here we may discriminate. When a woman has been seriously +wronged by a man she no longer oscillates, and the usual symptom +is a broken bell wire. Here we may take it that there is a love +matter, but that the maiden is not so much angry as perplexed, or +grieved. But here she comes in person to resolve our doubts." + +As he spoke there was a tap at the door, and the boy in buttons +entered to announce Miss Mary Sutherland, while the lady herself +loomed behind his small black figure like a full-sailed +merchant-man behind a tiny pilot boat. Sherlock Holmes welcomed +her with the easy courtesy for which he was remarkable, and, +having closed the door and bowed her into an armchair, he looked +her over in the minute and yet abstracted fashion which was +peculiar to him. + +"Do you not find," he said, "that with your short sight it is a +little trying to do so much typewriting?" + +"I did at first," she answered, "but now I know where the letters +are without looking." Then, suddenly realising the full purport +of his words, she gave a violent start and looked up, with fear +and astonishment upon her broad, good-humoured face. "You've +heard about me, Mr. Holmes," she cried, "else how could you know +all that?" + +"Never mind," said Holmes, laughing; "it is my business to know +things. Perhaps I have trained myself to see what others +overlook. If not, why should you come to consult me?" + +"I came to you, sir, because I heard of you from Mrs. Etherege, +whose husband you found so easy when the police and everyone had +given him up for dead. Oh, Mr. Holmes, I wish you would do as +much for me. I'm not rich, but still I have a hundred a year in +my own right, besides the little that I make by the machine, and +I would give it all to know what has become of Mr. Hosmer Angel." + +"Why did you come away to consult me in such a hurry?" asked +Sherlock Holmes, with his finger-tips together and his eyes to +the ceiling. + +Again a startled look came over the somewhat vacuous face of Miss +Mary Sutherland. "Yes, I did bang out of the house," she said, +"for it made me angry to see the easy way in which Mr. +Windibank--that is, my father--took it all. He would not go to +the police, and he would not go to you, and so at last, as he +would do nothing and kept on saying that there was no harm done, +it made me mad, and I just on with my things and came right away +to you." + +"Your father," said Holmes, "your stepfather, surely, since the +name is different." + +"Yes, my stepfather. I call him father, though it sounds funny, +too, for he is only five years and two months older than myself." + +"And your mother is alive?" + +"Oh, yes, mother is alive and well. I wasn't best pleased, Mr. +Holmes, when she married again so soon after father's death, and +a man who was nearly fifteen years younger than herself. Father +was a plumber in the Tottenham Court Road, and he left a tidy +business behind him, which mother carried on with Mr. Hardy, the +foreman; but when Mr. Windibank came he made her sell the +business, for he was very superior, being a traveller in wines. +They got 4700 pounds for the goodwill and interest, which wasn't +near as much as father could have got if he had been alive." + +I had expected to see Sherlock Holmes impatient under this +rambling and inconsequential narrative, but, on the contrary, he +had listened with the greatest concentration of attention. + +"Your own little income," he asked, "does it come out of the +business?" + +"Oh, no, sir. It is quite separate and was left me by my uncle +Ned in Auckland. It is in New Zealand stock, paying 4 1/2 per +cent. Two thousand five hundred pounds was the amount, but I can +only touch the interest." + +"You interest me extremely," said Holmes. "And since you draw so +large a sum as a hundred a year, with what you earn into the +bargain, you no doubt travel a little and indulge yourself in +every way. I believe that a single lady can get on very nicely +upon an income of about 60 pounds." + +"I could do with much less than that, Mr. Holmes, but you +understand that as long as I live at home I don't wish to be a +burden to them, and so they have the use of the money just while +I am staying with them. Of course, that is only just for the +time. Mr. Windibank draws my interest every quarter and pays it +over to mother, and I find that I can do pretty well with what I +earn at typewriting. It brings me twopence a sheet, and I can +often do from fifteen to twenty sheets in a day." + +"You have made your position very clear to me," said Holmes. +"This is my friend, Dr. Watson, before whom you can speak as +freely as before myself. Kindly tell us now all about your +connection with Mr. Hosmer Angel." + +A flush stole over Miss Sutherland's face, and she picked +nervously at the fringe of her jacket. "I met him first at the +gasfitters' ball," she said. "They used to send father tickets +when he was alive, and then afterwards they remembered us, and +sent them to mother. Mr. Windibank did not wish us to go. He +never did wish us to go anywhere. He would get quite mad if I +wanted so much as to join a Sunday-school treat. But this time I +was set on going, and I would go; for what right had he to +prevent? He said the folk were not fit for us to know, when all +father's friends were to be there. And he said that I had nothing +fit to wear, when I had my purple plush that I had never so much +as taken out of the drawer. At last, when nothing else would do, +he went off to France upon the business of the firm, but we went, +mother and I, with Mr. Hardy, who used to be our foreman, and it +was there I met Mr. Hosmer Angel." + +"I suppose," said Holmes, "that when Mr. Windibank came back from +France he was very annoyed at your having gone to the ball." + +"Oh, well, he was very good about it. He laughed, I remember, and +shrugged his shoulders, and said there was no use denying +anything to a woman, for she would have her way." + +"I see. Then at the gasfitters' ball you met, as I understand, a +gentleman called Mr. Hosmer Angel." + +"Yes, sir. I met him that night, and he called next day to ask if +we had got home all safe, and after that we met him--that is to +say, Mr. Holmes, I met him twice for walks, but after that father +came back again, and Mr. Hosmer Angel could not come to the house +any more." + +"No?" + +"Well, you know father didn't like anything of the sort. He +wouldn't have any visitors if he could help it, and he used to +say that a woman should be happy in her own family circle. But +then, as I used to say to mother, a woman wants her own circle to +begin with, and I had not got mine yet." + +"But how about Mr. Hosmer Angel? Did he make no attempt to see +you?" + +"Well, father was going off to France again in a week, and Hosmer +wrote and said that it would be safer and better not to see each +other until he had gone. We could write in the meantime, and he +used to write every day. I took the letters in in the morning, so +there was no need for father to know." + +"Were you engaged to the gentleman at this time?" + +"Oh, yes, Mr. Holmes. We were engaged after the first walk that +we took. Hosmer--Mr. Angel--was a cashier in an office in +Leadenhall Street--and--" + +"What office?" + +"That's the worst of it, Mr. Holmes, I don't know." + +"Where did he live, then?" + +"He slept on the premises." + +"And you don't know his address?" + +"No--except that it was Leadenhall Street." + +"Where did you address your letters, then?" + +"To the Leadenhall Street Post Office, to be left till called +for. He said that if they were sent to the office he would be +chaffed by all the other clerks about having letters from a lady, +so I offered to typewrite them, like he did his, but he wouldn't +have that, for he said that when I wrote them they seemed to come +from me, but when they were typewritten he always felt that the +machine had come between us. That will just show you how fond he +was of me, Mr. Holmes, and the little things that he would think +of." + +"It was most suggestive," said Holmes. "It has long been an axiom +of mine that the little things are infinitely the most important. +Can you remember any other little things about Mr. Hosmer Angel?" + +"He was a very shy man, Mr. Holmes. He would rather walk with me +in the evening than in the daylight, for he said that he hated to +be conspicuous. Very retiring and gentlemanly he was. Even his +voice was gentle. He'd had the quinsy and swollen glands when he +was young, he told me, and it had left him with a weak throat, +and a hesitating, whispering fashion of speech. He was always +well dressed, very neat and plain, but his eyes were weak, just +as mine are, and he wore tinted glasses against the glare." + +"Well, and what happened when Mr. Windibank, your stepfather, +returned to France?" + +"Mr. Hosmer Angel came to the house again and proposed that we +should marry before father came back. He was in dreadful earnest +and made me swear, with my hands on the Testament, that whatever +happened I would always be true to him. Mother said he was quite +right to make me swear, and that it was a sign of his passion. +Mother was all in his favour from the first and was even fonder +of him than I was. Then, when they talked of marrying within the +week, I began to ask about father; but they both said never to +mind about father, but just to tell him afterwards, and mother +said she would make it all right with him. I didn't quite like +that, Mr. Holmes. It seemed funny that I should ask his leave, as +he was only a few years older than me; but I didn't want to do +anything on the sly, so I wrote to father at Bordeaux, where the +company has its French offices, but the letter came back to me on +the very morning of the wedding." + +"It missed him, then?" + +"Yes, sir; for he had started to England just before it arrived." + +"Ha! that was unfortunate. Your wedding was arranged, then, for +the Friday. Was it to be in church?" + +"Yes, sir, but very quietly. It was to be at St. Saviour's, near +King's Cross, and we were to have breakfast afterwards at the St. +Pancras Hotel. Hosmer came for us in a hansom, but as there were +two of us he put us both into it and stepped himself into a +four-wheeler, which happened to be the only other cab in the +street. We got to the church first, and when the four-wheeler +drove up we waited for him to step out, but he never did, and +when the cabman got down from the box and looked there was no one +there! The cabman said that he could not imagine what had become +of him, for he had seen him get in with his own eyes. That was +last Friday, Mr. Holmes, and I have never seen or heard anything +since then to throw any light upon what became of him." + +"It seems to me that you have been very shamefully treated," said +Holmes. + +"Oh, no, sir! He was too good and kind to leave me so. Why, all +the morning he was saying to me that, whatever happened, I was to +be true; and that even if something quite unforeseen occurred to +separate us, I was always to remember that I was pledged to him, +and that he would claim his pledge sooner or later. It seemed +strange talk for a wedding-morning, but what has happened since +gives a meaning to it." + +"Most certainly it does. Your own opinion is, then, that some +unforeseen catastrophe has occurred to him?" + +"Yes, sir. I believe that he foresaw some danger, or else he +would not have talked so. And then I think that what he foresaw +happened." + +"But you have no notion as to what it could have been?" + +"None." + +"One more question. How did your mother take the matter?" + +"She was angry, and said that I was never to speak of the matter +again." + +"And your father? Did you tell him?" + +"Yes; and he seemed to think, with me, that something had +happened, and that I should hear of Hosmer again. As he said, +what interest could anyone have in bringing me to the doors of +the church, and then leaving me? Now, if he had borrowed my +money, or if he had married me and got my money settled on him, +there might be some reason, but Hosmer was very independent about +money and never would look at a shilling of mine. And yet, what +could have happened? And why could he not write? Oh, it drives me +half-mad to think of it, and I can't sleep a wink at night." She +pulled a little handkerchief out of her muff and began to sob +heavily into it. + +"I shall glance into the case for you," said Holmes, rising, "and +I have no doubt that we shall reach some definite result. Let the +weight of the matter rest upon me now, and do not let your mind +dwell upon it further. Above all, try to let Mr. Hosmer Angel +vanish from your memory, as he has done from your life." + +"Then you don't think I'll see him again?" + +"I fear not." + +"Then what has happened to him?" + +"You will leave that question in my hands. I should like an +accurate description of him and any letters of his which you can +spare." + +"I advertised for him in last Saturday's Chronicle," said she. +"Here is the slip and here are four letters from him." + +"Thank you. And your address?" + +"No. 31 Lyon Place, Camberwell." + +"Mr. Angel's address you never had, I understand. Where is your +father's place of business?" + +"He travels for Westhouse & Marbank, the great claret importers +of Fenchurch Street." + +"Thank you. You have made your statement very clearly. You will +leave the papers here, and remember the advice which I have given +you. Let the whole incident be a sealed book, and do not allow it +to affect your life." + +"You are very kind, Mr. Holmes, but I cannot do that. I shall be +true to Hosmer. He shall find me ready when he comes back." + +For all the preposterous hat and the vacuous face, there was +something noble in the simple faith of our visitor which +compelled our respect. She laid her little bundle of papers upon +the table and went her way, with a promise to come again whenever +she might be summoned. + +Sherlock Holmes sat silent for a few minutes with his fingertips +still pressed together, his legs stretched out in front of him, +and his gaze directed upward to the ceiling. Then he took down +from the rack the old and oily clay pipe, which was to him as a +counsellor, and, having lit it, he leaned back in his chair, with +the thick blue cloud-wreaths spinning up from him, and a look of +infinite languor in his face. + +"Quite an interesting study, that maiden," he observed. "I found +her more interesting than her little problem, which, by the way, +is rather a trite one. You will find parallel cases, if you +consult my index, in Andover in '77, and there was something of +the sort at The Hague last year. Old as is the idea, however, +there were one or two details which were new to me. But the +maiden herself was most instructive." + +"You appeared to read a good deal upon her which was quite +invisible to me," I remarked. + +"Not invisible but unnoticed, Watson. You did not know where to +look, and so you missed all that was important. I can never bring +you to realise the importance of sleeves, the suggestiveness of +thumb-nails, or the great issues that may hang from a boot-lace. +Now, what did you gather from that woman's appearance? Describe +it." + +"Well, she had a slate-coloured, broad-brimmed straw hat, with a +feather of a brickish red. Her jacket was black, with black beads +sewn upon it, and a fringe of little black jet ornaments. Her +dress was brown, rather darker than coffee colour, with a little +purple plush at the neck and sleeves. Her gloves were greyish and +were worn through at the right forefinger. Her boots I didn't +observe. She had small round, hanging gold earrings, and a +general air of being fairly well-to-do in a vulgar, comfortable, +easy-going way." + +Sherlock Holmes clapped his hands softly together and chuckled. + +"'Pon my word, Watson, you are coming along wonderfully. You have +really done very well indeed. It is true that you have missed +everything of importance, but you have hit upon the method, and +you have a quick eye for colour. Never trust to general +impressions, my boy, but concentrate yourself upon details. My +first glance is always at a woman's sleeve. In a man it is +perhaps better first to take the knee of the trouser. As you +observe, this woman had plush upon her sleeves, which is a most +useful material for showing traces. The double line a little +above the wrist, where the typewritist presses against the table, +was beautifully defined. The sewing-machine, of the hand type, +leaves a similar mark, but only on the left arm, and on the side +of it farthest from the thumb, instead of being right across the +broadest part, as this was. I then glanced at her face, and, +observing the dint of a pince-nez at either side of her nose, I +ventured a remark upon short sight and typewriting, which seemed +to surprise her." + +"It surprised me." + +"But, surely, it was obvious. I was then much surprised and +interested on glancing down to observe that, though the boots +which she was wearing were not unlike each other, they were +really odd ones; the one having a slightly decorated toe-cap, and +the other a plain one. One was buttoned only in the two lower +buttons out of five, and the other at the first, third, and +fifth. Now, when you see that a young lady, otherwise neatly +dressed, has come away from home with odd boots, half-buttoned, +it is no great deduction to say that she came away in a hurry." + +"And what else?" I asked, keenly interested, as I always was, by +my friend's incisive reasoning. + +"I noted, in passing, that she had written a note before leaving +home but after being fully dressed. You observed that her right +glove was torn at the forefinger, but you did not apparently see +that both glove and finger were stained with violet ink. She had +written in a hurry and dipped her pen too deep. It must have been +this morning, or the mark would not remain clear upon the finger. +All this is amusing, though rather elementary, but I must go back +to business, Watson. Would you mind reading me the advertised +description of Mr. Hosmer Angel?" + +I held the little printed slip to the light. + +"Missing," it said, "on the morning of the fourteenth, a gentleman +named Hosmer Angel. About five ft. seven in. in height; +strongly built, sallow complexion, black hair, a little bald in +the centre, bushy, black side-whiskers and moustache; tinted +glasses, slight infirmity of speech. Was dressed, when last seen, +in black frock-coat faced with silk, black waistcoat, gold Albert +chain, and grey Harris tweed trousers, with brown gaiters over +elastic-sided boots. Known to have been employed in an office in +Leadenhall Street. Anybody bringing--" + +"That will do," said Holmes. "As to the letters," he continued, +glancing over them, "they are very commonplace. Absolutely no +clue in them to Mr. Angel, save that he quotes Balzac once. There +is one remarkable point, however, which will no doubt strike +you." + +"They are typewritten," I remarked. + +"Not only that, but the signature is typewritten. Look at the +neat little 'Hosmer Angel' at the bottom. There is a date, you +see, but no superscription except Leadenhall Street, which is +rather vague. The point about the signature is very suggestive--in +fact, we may call it conclusive." + +"Of what?" + +"My dear fellow, is it possible you do not see how strongly it +bears upon the case?" + +"I cannot say that I do unless it were that he wished to be able +to deny his signature if an action for breach of promise were +instituted." + +"No, that was not the point. However, I shall write two letters, +which should settle the matter. One is to a firm in the City, the +other is to the young lady's stepfather, Mr. Windibank, asking +him whether he could meet us here at six o'clock tomorrow +evening. It is just as well that we should do business with the +male relatives. And now, Doctor, we can do nothing until the +answers to those letters come, so we may put our little problem +upon the shelf for the interim." + +I had had so many reasons to believe in my friend's subtle powers +of reasoning and extraordinary energy in action that I felt that +he must have some solid grounds for the assured and easy +demeanour with which he treated the singular mystery which he had +been called upon to fathom. Once only had I known him to fail, in +the case of the King of Bohemia and of the Irene Adler +photograph; but when I looked back to the weird business of the +Sign of Four, and the extraordinary circumstances connected with +the Study in Scarlet, I felt that it would be a strange tangle +indeed which he could not unravel. + +I left him then, still puffing at his black clay pipe, with the +conviction that when I came again on the next evening I would +find that he held in his hands all the clues which would lead up +to the identity of the disappearing bridegroom of Miss Mary +Sutherland. + +A professional case of great gravity was engaging my own +attention at the time, and the whole of next day I was busy at +the bedside of the sufferer. It was not until close upon six +o'clock that I found myself free and was able to spring into a +hansom and drive to Baker Street, half afraid that I might be too +late to assist at the dénouement of the little mystery. I found +Sherlock Holmes alone, however, half asleep, with his long, thin +form curled up in the recesses of his armchair. A formidable +array of bottles and test-tubes, with the pungent cleanly smell +of hydrochloric acid, told me that he had spent his day in the +chemical work which was so dear to him. + +"Well, have you solved it?" I asked as I entered. + +"Yes. It was the bisulphate of baryta." + +"No, no, the mystery!" I cried. + +"Oh, that! I thought of the salt that I have been working upon. +There was never any mystery in the matter, though, as I said +yesterday, some of the details are of interest. The only drawback +is that there is no law, I fear, that can touch the scoundrel." + +"Who was he, then, and what was his object in deserting Miss +Sutherland?" + +The question was hardly out of my mouth, and Holmes had not yet +opened his lips to reply, when we heard a heavy footfall in the +passage and a tap at the door. + +"This is the girl's stepfather, Mr. James Windibank," said +Holmes. "He has written to me to say that he would be here at +six. Come in!" + +The man who entered was a sturdy, middle-sized fellow, some +thirty years of age, clean-shaven, and sallow-skinned, with a +bland, insinuating manner, and a pair of wonderfully sharp and +penetrating grey eyes. He shot a questioning glance at each of +us, placed his shiny top-hat upon the sideboard, and with a +slight bow sidled down into the nearest chair. + +"Good-evening, Mr. James Windibank," said Holmes. "I think that +this typewritten letter is from you, in which you made an +appointment with me for six o'clock?" + +"Yes, sir. I am afraid that I am a little late, but I am not +quite my own master, you know. I am sorry that Miss Sutherland +has troubled you about this little matter, for I think it is far +better not to wash linen of the sort in public. It was quite +against my wishes that she came, but she is a very excitable, +impulsive girl, as you may have noticed, and she is not easily +controlled when she has made up her mind on a point. Of course, I +did not mind you so much, as you are not connected with the +official police, but it is not pleasant to have a family +misfortune like this noised abroad. Besides, it is a useless +expense, for how could you possibly find this Hosmer Angel?" + +"On the contrary," said Holmes quietly; "I have every reason to +believe that I will succeed in discovering Mr. Hosmer Angel." + +Mr. Windibank gave a violent start and dropped his gloves. "I am +delighted to hear it," he said. + +"It is a curious thing," remarked Holmes, "that a typewriter has +really quite as much individuality as a man's handwriting. Unless +they are quite new, no two of them write exactly alike. Some +letters get more worn than others, and some wear only on one +side. Now, you remark in this note of yours, Mr. Windibank, that +in every case there is some little slurring over of the 'e,' and +a slight defect in the tail of the 'r.' There are fourteen other +characteristics, but those are the more obvious." + +"We do all our correspondence with this machine at the office, +and no doubt it is a little worn," our visitor answered, glancing +keenly at Holmes with his bright little eyes. + +"And now I will show you what is really a very interesting study, +Mr. Windibank," Holmes continued. "I think of writing another +little monograph some of these days on the typewriter and its +relation to crime. It is a subject to which I have devoted some +little attention. I have here four letters which purport to come +from the missing man. They are all typewritten. In each case, not +only are the 'e's' slurred and the 'r's' tailless, but you will +observe, if you care to use my magnifying lens, that the fourteen +other characteristics to which I have alluded are there as well." + +Mr. Windibank sprang out of his chair and picked up his hat. "I +cannot waste time over this sort of fantastic talk, Mr. Holmes," +he said. "If you can catch the man, catch him, and let me know +when you have done it." + +"Certainly," said Holmes, stepping over and turning the key in +the door. "I let you know, then, that I have caught him!" + +"What! where?" shouted Mr. Windibank, turning white to his lips +and glancing about him like a rat in a trap. + +"Oh, it won't do--really it won't," said Holmes suavely. "There +is no possible getting out of it, Mr. Windibank. It is quite too +transparent, and it was a very bad compliment when you said that +it was impossible for me to solve so simple a question. That's +right! Sit down and let us talk it over." + +Our visitor collapsed into a chair, with a ghastly face and a +glitter of moisture on his brow. "It--it's not actionable," he +stammered. + +"I am very much afraid that it is not. But between ourselves, +Windibank, it was as cruel and selfish and heartless a trick in a +petty way as ever came before me. Now, let me just run over the +course of events, and you will contradict me if I go wrong." + +The man sat huddled up in his chair, with his head sunk upon his +breast, like one who is utterly crushed. Holmes stuck his feet up +on the corner of the mantelpiece and, leaning back with his hands +in his pockets, began talking, rather to himself, as it seemed, +than to us. + +"The man married a woman very much older than himself for her +money," said he, "and he enjoyed the use of the money of the +daughter as long as she lived with them. It was a considerable +sum, for people in their position, and the loss of it would have +made a serious difference. It was worth an effort to preserve it. +The daughter was of a good, amiable disposition, but affectionate +and warm-hearted in her ways, so that it was evident that with +her fair personal advantages, and her little income, she would +not be allowed to remain single long. Now her marriage would +mean, of course, the loss of a hundred a year, so what does her +stepfather do to prevent it? He takes the obvious course of +keeping her at home and forbidding her to seek the company of +people of her own age. But soon he found that that would not +answer forever. She became restive, insisted upon her rights, and +finally announced her positive intention of going to a certain +ball. What does her clever stepfather do then? He conceives an +idea more creditable to his head than to his heart. With the +connivance and assistance of his wife he disguised himself, +covered those keen eyes with tinted glasses, masked the face with +a moustache and a pair of bushy whiskers, sunk that clear voice +into an insinuating whisper, and doubly secure on account of the +girl's short sight, he appears as Mr. Hosmer Angel, and keeps off +other lovers by making love himself." + +"It was only a joke at first," groaned our visitor. "We never +thought that she would have been so carried away." + +"Very likely not. However that may be, the young lady was very +decidedly carried away, and, having quite made up her mind that +her stepfather was in France, the suspicion of treachery never +for an instant entered her mind. She was flattered by the +gentleman's attentions, and the effect was increased by the +loudly expressed admiration of her mother. Then Mr. Angel began +to call, for it was obvious that the matter should be pushed as +far as it would go if a real effect were to be produced. There +were meetings, and an engagement, which would finally secure the +girl's affections from turning towards anyone else. But the +deception could not be kept up forever. These pretended journeys +to France were rather cumbrous. The thing to do was clearly to +bring the business to an end in such a dramatic manner that it +would leave a permanent impression upon the young lady's mind and +prevent her from looking upon any other suitor for some time to +come. Hence those vows of fidelity exacted upon a Testament, and +hence also the allusions to a possibility of something happening +on the very morning of the wedding. James Windibank wished Miss +Sutherland to be so bound to Hosmer Angel, and so uncertain as to +his fate, that for ten years to come, at any rate, she would not +listen to another man. As far as the church door he brought her, +and then, as he could go no farther, he conveniently vanished +away by the old trick of stepping in at one door of a +four-wheeler and out at the other. I think that was the chain of +events, Mr. Windibank!" + +Our visitor had recovered something of his assurance while Holmes +had been talking, and he rose from his chair now with a cold +sneer upon his pale face. + +"It may be so, or it may not, Mr. Holmes," said he, "but if you +are so very sharp you ought to be sharp enough to know that it is +you who are breaking the law now, and not me. I have done nothing +actionable from the first, but as long as you keep that door +locked you lay yourself open to an action for assault and illegal +constraint." + +"The law cannot, as you say, touch you," said Holmes, unlocking +and throwing open the door, "yet there never was a man who +deserved punishment more. If the young lady has a brother or a +friend, he ought to lay a whip across your shoulders. By Jove!" +he continued, flushing up at the sight of the bitter sneer upon +the man's face, "it is not part of my duties to my client, but +here's a hunting crop handy, and I think I shall just treat +myself to--" He took two swift steps to the whip, but before he +could grasp it there was a wild clatter of steps upon the stairs, +the heavy hall door banged, and from the window we could see Mr. +James Windibank running at the top of his speed down the road. + +"There's a cold-blooded scoundrel!" said Holmes, laughing, as he +threw himself down into his chair once more. "That fellow will +rise from crime to crime until he does something very bad, and +ends on a gallows. The case has, in some respects, been not +entirely devoid of interest." + +"I cannot now entirely see all the steps of your reasoning," I +remarked. + +"Well, of course it was obvious from the first that this Mr. +Hosmer Angel must have some strong object for his curious +conduct, and it was equally clear that the only man who really +profited by the incident, as far as we could see, was the +stepfather. Then the fact that the two men were never together, +but that the one always appeared when the other was away, was +suggestive. So were the tinted spectacles and the curious voice, +which both hinted at a disguise, as did the bushy whiskers. My +suspicions were all confirmed by his peculiar action in +typewriting his signature, which, of course, inferred that his +handwriting was so familiar to her that she would recognise even +the smallest sample of it. You see all these isolated facts, +together with many minor ones, all pointed in the same +direction." + +"And how did you verify them?" + +"Having once spotted my man, it was easy to get corroboration. I +knew the firm for which this man worked. Having taken the printed +description. I eliminated everything from it which could be the +result of a disguise--the whiskers, the glasses, the voice, and I +sent it to the firm, with a request that they would inform me +whether it answered to the description of any of their +travellers. I had already noticed the peculiarities of the +typewriter, and I wrote to the man himself at his business +address asking him if he would come here. As I expected, his +reply was typewritten and revealed the same trivial but +characteristic defects. The same post brought me a letter from +Westhouse & Marbank, of Fenchurch Street, to say that the +description tallied in every respect with that of their employé, +James Windibank. Voilà tout!" + +"And Miss Sutherland?" + +"If I tell her she will not believe me. You may remember the old +Persian saying, 'There is danger for him who taketh the tiger +cub, and danger also for whoso snatches a delusion from a woman.' +There is as much sense in Hafiz as in Horace, and as much +knowledge of the world." + + + +ADVENTURE IV. THE BOSCOMBE VALLEY MYSTERY + +We were seated at breakfast one morning, my wife and I, when the +maid brought in a telegram. It was from Sherlock Holmes and ran +in this way: + +"Have you a couple of days to spare? Have just been wired for from +the west of England in connection with Boscombe Valley tragedy. +Shall be glad if you will come with me. Air and scenery perfect. +Leave Paddington by the 11:15." + +"What do you say, dear?" said my wife, looking across at me. +"Will you go?" + +"I really don't know what to say. I have a fairly long list at +present." + +"Oh, Anstruther would do your work for you. You have been looking +a little pale lately. I think that the change would do you good, +and you are always so interested in Mr. Sherlock Holmes' cases." + +"I should be ungrateful if I were not, seeing what I gained +through one of them," I answered. "But if I am to go, I must pack +at once, for I have only half an hour." + +My experience of camp life in Afghanistan had at least had the +effect of making me a prompt and ready traveller. My wants were +few and simple, so that in less than the time stated I was in a +cab with my valise, rattling away to Paddington Station. Sherlock +Holmes was pacing up and down the platform, his tall, gaunt +figure made even gaunter and taller by his long grey +travelling-cloak and close-fitting cloth cap. + +"It is really very good of you to come, Watson," said he. "It +makes a considerable difference to me, having someone with me on +whom I can thoroughly rely. Local aid is always either worthless +or else biassed. If you will keep the two corner seats I shall +get the tickets." + +We had the carriage to ourselves save for an immense litter of +papers which Holmes had brought with him. Among these he rummaged +and read, with intervals of note-taking and of meditation, until +we were past Reading. Then he suddenly rolled them all into a +gigantic ball and tossed them up onto the rack. + +"Have you heard anything of the case?" he asked. + +"Not a word. I have not seen a paper for some days." + +"The London press has not had very full accounts. I have just +been looking through all the recent papers in order to master the +particulars. It seems, from what I gather, to be one of those +simple cases which are so extremely difficult." + +"That sounds a little paradoxical." + +"But it is profoundly true. Singularity is almost invariably a +clue. The more featureless and commonplace a crime is, the more +difficult it is to bring it home. In this case, however, they +have established a very serious case against the son of the +murdered man." + +"It is a murder, then?" + +"Well, it is conjectured to be so. I shall take nothing for +granted until I have the opportunity of looking personally into +it. I will explain the state of things to you, as far as I have +been able to understand it, in a very few words. + +"Boscombe Valley is a country district not very far from Ross, in +Herefordshire. The largest landed proprietor in that part is a +Mr. John Turner, who made his money in Australia and returned +some years ago to the old country. One of the farms which he +held, that of Hatherley, was let to Mr. Charles McCarthy, who was +also an ex-Australian. The men had known each other in the +colonies, so that it was not unnatural that when they came to +settle down they should do so as near each other as possible. +Turner was apparently the richer man, so McCarthy became his +tenant but still remained, it seems, upon terms of perfect +equality, as they were frequently together. McCarthy had one son, +a lad of eighteen, and Turner had an only daughter of the same +age, but neither of them had wives living. They appear to have +avoided the society of the neighbouring English families and to +have led retired lives, though both the McCarthys were fond of +sport and were frequently seen at the race-meetings of the +neighbourhood. McCarthy kept two servants--a man and a girl. +Turner had a considerable household, some half-dozen at the +least. That is as much as I have been able to gather about the +families. Now for the facts. + +"On June 3rd, that is, on Monday last, McCarthy left his house at +Hatherley about three in the afternoon and walked down to the +Boscombe Pool, which is a small lake formed by the spreading out +of the stream which runs down the Boscombe Valley. He had been +out with his serving-man in the morning at Ross, and he had told +the man that he must hurry, as he had an appointment of +importance to keep at three. From that appointment he never came +back alive. + +"From Hatherley Farm-house to the Boscombe Pool is a quarter of a +mile, and two people saw him as he passed over this ground. One +was an old woman, whose name is not mentioned, and the other was +William Crowder, a game-keeper in the employ of Mr. Turner. Both +these witnesses depose that Mr. McCarthy was walking alone. The +game-keeper adds that within a few minutes of his seeing Mr. +McCarthy pass he had seen his son, Mr. James McCarthy, going the +same way with a gun under his arm. To the best of his belief, the +father was actually in sight at the time, and the son was +following him. He thought no more of the matter until he heard in +the evening of the tragedy that had occurred. + +"The two McCarthys were seen after the time when William Crowder, +the game-keeper, lost sight of them. The Boscombe Pool is thickly +wooded round, with just a fringe of grass and of reeds round the +edge. A girl of fourteen, Patience Moran, who is the daughter of +the lodge-keeper of the Boscombe Valley estate, was in one of the +woods picking flowers. She states that while she was there she +saw, at the border of the wood and close by the lake, Mr. +McCarthy and his son, and that they appeared to be having a +violent quarrel. She heard Mr. McCarthy the elder using very +strong language to his son, and she saw the latter raise up his +hand as if to strike his father. She was so frightened by their +violence that she ran away and told her mother when she reached +home that she had left the two McCarthys quarrelling near +Boscombe Pool, and that she was afraid that they were going to +fight. She had hardly said the words when young Mr. McCarthy came +running up to the lodge to say that he had found his father dead +in the wood, and to ask for the help of the lodge-keeper. He was +much excited, without either his gun or his hat, and his right +hand and sleeve were observed to be stained with fresh blood. On +following him they found the dead body stretched out upon the +grass beside the pool. The head had been beaten in by repeated +blows of some heavy and blunt weapon. The injuries were such as +might very well have been inflicted by the butt-end of his son's +gun, which was found lying on the grass within a few paces of the +body. Under these circumstances the young man was instantly +arrested, and a verdict of 'wilful murder' having been returned +at the inquest on Tuesday, he was on Wednesday brought before the +magistrates at Ross, who have referred the case to the next +Assizes. Those are the main facts of the case as they came out +before the coroner and the police-court." + +"I could hardly imagine a more damning case," I remarked. "If +ever circumstantial evidence pointed to a criminal it does so +here." + +"Circumstantial evidence is a very tricky thing," answered Holmes +thoughtfully. "It may seem to point very straight to one thing, +but if you shift your own point of view a little, you may find it +pointing in an equally uncompromising manner to something +entirely different. It must be confessed, however, that the case +looks exceedingly grave against the young man, and it is very +possible that he is indeed the culprit. There are several people +in the neighbourhood, however, and among them Miss Turner, the +daughter of the neighbouring landowner, who believe in his +innocence, and who have retained Lestrade, whom you may recollect +in connection with the Study in Scarlet, to work out the case in +his interest. Lestrade, being rather puzzled, has referred the +case to me, and hence it is that two middle-aged gentlemen are +flying westward at fifty miles an hour instead of quietly +digesting their breakfasts at home." + +"I am afraid," said I, "that the facts are so obvious that you +will find little credit to be gained out of this case." + +"There is nothing more deceptive than an obvious fact," he +answered, laughing. "Besides, we may chance to hit upon some +other obvious facts which may have been by no means obvious to +Mr. Lestrade. You know me too well to think that I am boasting +when I say that I shall either confirm or destroy his theory by +means which he is quite incapable of employing, or even of +understanding. To take the first example to hand, I very clearly +perceive that in your bedroom the window is upon the right-hand +side, and yet I question whether Mr. Lestrade would have noted +even so self-evident a thing as that." + +"How on earth--" + +"My dear fellow, I know you well. I know the military neatness +which characterises you. You shave every morning, and in this +season you shave by the sunlight; but since your shaving is less +and less complete as we get farther back on the left side, until +it becomes positively slovenly as we get round the angle of the +jaw, it is surely very clear that that side is less illuminated +than the other. I could not imagine a man of your habits looking +at himself in an equal light and being satisfied with such a +result. I only quote this as a trivial example of observation and +inference. Therein lies my métier, and it is just possible that +it may be of some service in the investigation which lies before +us. There are one or two minor points which were brought out in +the inquest, and which are worth considering." + +"What are they?" + +"It appears that his arrest did not take place at once, but after +the return to Hatherley Farm. On the inspector of constabulary +informing him that he was a prisoner, he remarked that he was not +surprised to hear it, and that it was no more than his deserts. +This observation of his had the natural effect of removing any +traces of doubt which might have remained in the minds of the +coroner's jury." + +"It was a confession," I ejaculated. + +"No, for it was followed by a protestation of innocence." + +"Coming on the top of such a damning series of events, it was at +least a most suspicious remark." + +"On the contrary," said Holmes, "it is the brightest rift which I +can at present see in the clouds. However innocent he might be, +he could not be such an absolute imbecile as not to see that the +circumstances were very black against him. Had he appeared +surprised at his own arrest, or feigned indignation at it, I +should have looked upon it as highly suspicious, because such +surprise or anger would not be natural under the circumstances, +and yet might appear to be the best policy to a scheming man. His +frank acceptance of the situation marks him as either an innocent +man, or else as a man of considerable self-restraint and +firmness. As to his remark about his deserts, it was also not +unnatural if you consider that he stood beside the dead body of +his father, and that there is no doubt that he had that very day +so far forgotten his filial duty as to bandy words with him, and +even, according to the little girl whose evidence is so +important, to raise his hand as if to strike him. The +self-reproach and contrition which are displayed in his remark +appear to me to be the signs of a healthy mind rather than of a +guilty one." + +I shook my head. "Many men have been hanged on far slighter +evidence," I remarked. + +"So they have. And many men have been wrongfully hanged." + +"What is the young man's own account of the matter?" + +"It is, I am afraid, not very encouraging to his supporters, +though there are one or two points in it which are suggestive. +You will find it here, and may read it for yourself." + +He picked out from his bundle a copy of the local Herefordshire +paper, and having turned down the sheet he pointed out the +paragraph in which the unfortunate young man had given his own +statement of what had occurred. I settled myself down in the +corner of the carriage and read it very carefully. It ran in this +way: + +"Mr. James McCarthy, the only son of the deceased, was then called +and gave evidence as follows: 'I had been away from home for +three days at Bristol, and had only just returned upon the +morning of last Monday, the 3rd. My father was absent from home at +the time of my arrival, and I was informed by the maid that he +had driven over to Ross with John Cobb, the groom. Shortly after +my return I heard the wheels of his trap in the yard, and, +looking out of my window, I saw him get out and walk rapidly out +of the yard, though I was not aware in which direction he was +going. I then took my gun and strolled out in the direction of +the Boscombe Pool, with the intention of visiting the rabbit +warren which is upon the other side. On my way I saw William +Crowder, the game-keeper, as he had stated in his evidence; but +he is mistaken in thinking that I was following my father. I had +no idea that he was in front of me. When about a hundred yards +from the pool I heard a cry of "Cooee!" which was a usual signal +between my father and myself. I then hurried forward, and found +him standing by the pool. He appeared to be much surprised at +seeing me and asked me rather roughly what I was doing there. A +conversation ensued which led to high words and almost to blows, +for my father was a man of a very violent temper. Seeing that his +passion was becoming ungovernable, I left him and returned +towards Hatherley Farm. I had not gone more than 150 yards, +however, when I heard a hideous outcry behind me, which caused me +to run back again. I found my father expiring upon the ground, +with his head terribly injured. I dropped my gun and held him in +my arms, but he almost instantly expired. I knelt beside him for +some minutes, and then made my way to Mr. Turner's lodge-keeper, +his house being the nearest, to ask for assistance. I saw no one +near my father when I returned, and I have no idea how he came by +his injuries. He was not a popular man, being somewhat cold and +forbidding in his manners, but he had, as far as I know, no +active enemies. I know nothing further of the matter.' + +"The Coroner: Did your father make any statement to you before +he died? + +"Witness: He mumbled a few words, but I could only catch some +allusion to a rat. + +"The Coroner: What did you understand by that? + +"Witness: It conveyed no meaning to me. I thought that he was +delirious. + +"The Coroner: What was the point upon which you and your father +had this final quarrel? + +"Witness: I should prefer not to answer. + +"The Coroner: I am afraid that I must press it. + +"Witness: It is really impossible for me to tell you. I can +assure you that it has nothing to do with the sad tragedy which +followed. + +"The Coroner: That is for the court to decide. I need not point +out to you that your refusal to answer will prejudice your case +considerably in any future proceedings which may arise. + +"Witness: I must still refuse. + +"The Coroner: I understand that the cry of 'Cooee' was a common +signal between you and your father? + +"Witness: It was. + +"The Coroner: How was it, then, that he uttered it before he saw +you, and before he even knew that you had returned from Bristol? + +"Witness (with considerable confusion): I do not know. + +"A Juryman: Did you see nothing which aroused your suspicions +when you returned on hearing the cry and found your father +fatally injured? + +"Witness: Nothing definite. + +"The Coroner: What do you mean? + +"Witness: I was so disturbed and excited as I rushed out into +the open, that I could think of nothing except of my father. Yet +I have a vague impression that as I ran forward something lay +upon the ground to the left of me. It seemed to me to be +something grey in colour, a coat of some sort, or a plaid perhaps. +When I rose from my father I looked round for it, but it was +gone. + +"'Do you mean that it disappeared before you went for help?' + +"'Yes, it was gone.' + +"'You cannot say what it was?' + +"'No, I had a feeling something was there.' + +"'How far from the body?' + +"'A dozen yards or so.' + +"'And how far from the edge of the wood?' + +"'About the same.' + +"'Then if it was removed it was while you were within a dozen +yards of it?' + +"'Yes, but with my back towards it.' + +"This concluded the examination of the witness." + +"I see," said I as I glanced down the column, "that the coroner +in his concluding remarks was rather severe upon young McCarthy. +He calls attention, and with reason, to the discrepancy about his +father having signalled to him before seeing him, also to his +refusal to give details of his conversation with his father, and +his singular account of his father's dying words. They are all, +as he remarks, very much against the son." + +Holmes laughed softly to himself and stretched himself out upon +the cushioned seat. "Both you and the coroner have been at some +pains," said he, "to single out the very strongest points in the +young man's favour. Don't you see that you alternately give him +credit for having too much imagination and too little? Too +little, if he could not invent a cause of quarrel which would +give him the sympathy of the jury; too much, if he evolved from +his own inner consciousness anything so outré as a dying +reference to a rat, and the incident of the vanishing cloth. No, +sir, I shall approach this case from the point of view that what +this young man says is true, and we shall see whither that +hypothesis will lead us. And now here is my pocket Petrarch, and +not another word shall I say of this case until we are on the +scene of action. We lunch at Swindon, and I see that we shall be +there in twenty minutes." + +It was nearly four o'clock when we at last, after passing through +the beautiful Stroud Valley, and over the broad gleaming Severn, +found ourselves at the pretty little country-town of Ross. A +lean, ferret-like man, furtive and sly-looking, was waiting for +us upon the platform. In spite of the light brown dustcoat and +leather-leggings which he wore in deference to his rustic +surroundings, I had no difficulty in recognising Lestrade, of +Scotland Yard. With him we drove to the Hereford Arms where a +room had already been engaged for us. + +"I have ordered a carriage," said Lestrade as we sat over a cup +of tea. "I knew your energetic nature, and that you would not be +happy until you had been on the scene of the crime." + +"It was very nice and complimentary of you," Holmes answered. "It +is entirely a question of barometric pressure." + +Lestrade looked startled. "I do not quite follow," he said. + +"How is the glass? Twenty-nine, I see. No wind, and not a cloud +in the sky. I have a caseful of cigarettes here which need +smoking, and the sofa is very much superior to the usual country +hotel abomination. I do not think that it is probable that I +shall use the carriage to-night." + +Lestrade laughed indulgently. "You have, no doubt, already formed +your conclusions from the newspapers," he said. "The case is as +plain as a pikestaff, and the more one goes into it the plainer +it becomes. Still, of course, one can't refuse a lady, and such a +very positive one, too. She has heard of you, and would have your +opinion, though I repeatedly told her that there was nothing +which you could do which I had not already done. Why, bless my +soul! here is her carriage at the door." + +He had hardly spoken before there rushed into the room one of the +most lovely young women that I have ever seen in my life. Her +violet eyes shining, her lips parted, a pink flush upon her +cheeks, all thought of her natural reserve lost in her +overpowering excitement and concern. + +"Oh, Mr. Sherlock Holmes!" she cried, glancing from one to the +other of us, and finally, with a woman's quick intuition, +fastening upon my companion, "I am so glad that you have come. I +have driven down to tell you so. I know that James didn't do it. +I know it, and I want you to start upon your work knowing it, +too. Never let yourself doubt upon that point. We have known each +other since we were little children, and I know his faults as no +one else does; but he is too tender-hearted to hurt a fly. Such a +charge is absurd to anyone who really knows him." + +"I hope we may clear him, Miss Turner," said Sherlock Holmes. +"You may rely upon my doing all that I can." + +"But you have read the evidence. You have formed some conclusion? +Do you not see some loophole, some flaw? Do you not yourself +think that he is innocent?" + +"I think that it is very probable." + +"There, now!" she cried, throwing back her head and looking +defiantly at Lestrade. "You hear! He gives me hopes." + +Lestrade shrugged his shoulders. "I am afraid that my colleague +has been a little quick in forming his conclusions," he said. + +"But he is right. Oh! I know that he is right. James never did +it. And about his quarrel with his father, I am sure that the +reason why he would not speak about it to the coroner was because +I was concerned in it." + +"In what way?" asked Holmes. + +"It is no time for me to hide anything. James and his father had +many disagreements about me. Mr. McCarthy was very anxious that +there should be a marriage between us. James and I have always +loved each other as brother and sister; but of course he is young +and has seen very little of life yet, and--and--well, he +naturally did not wish to do anything like that yet. So there +were quarrels, and this, I am sure, was one of them." + +"And your father?" asked Holmes. "Was he in favour of such a +union?" + +"No, he was averse to it also. No one but Mr. McCarthy was in +favour of it." A quick blush passed over her fresh young face as +Holmes shot one of his keen, questioning glances at her. + +"Thank you for this information," said he. "May I see your father +if I call to-morrow?" + +"I am afraid the doctor won't allow it." + +"The doctor?" + +"Yes, have you not heard? Poor father has never been strong for +years back, but this has broken him down completely. He has taken +to his bed, and Dr. Willows says that he is a wreck and that his +nervous system is shattered. Mr. McCarthy was the only man alive +who had known dad in the old days in Victoria." + +"Ha! In Victoria! That is important." + +"Yes, at the mines." + +"Quite so; at the gold-mines, where, as I understand, Mr. Turner +made his money." + +"Yes, certainly." + +"Thank you, Miss Turner. You have been of material assistance to +me." + +"You will tell me if you have any news to-morrow. No doubt you +will go to the prison to see James. Oh, if you do, Mr. Holmes, do +tell him that I know him to be innocent." + +"I will, Miss Turner." + +"I must go home now, for dad is very ill, and he misses me so if +I leave him. Good-bye, and God help you in your undertaking." She +hurried from the room as impulsively as she had entered, and we +heard the wheels of her carriage rattle off down the street. + +"I am ashamed of you, Holmes," said Lestrade with dignity after a +few minutes' silence. "Why should you raise up hopes which you +are bound to disappoint? I am not over-tender of heart, but I +call it cruel." + +"I think that I see my way to clearing James McCarthy," said +Holmes. "Have you an order to see him in prison?" + +"Yes, but only for you and me." + +"Then I shall reconsider my resolution about going out. We have +still time to take a train to Hereford and see him to-night?" + +"Ample." + +"Then let us do so. Watson, I fear that you will find it very +slow, but I shall only be away a couple of hours." + +I walked down to the station with them, and then wandered through +the streets of the little town, finally returning to the hotel, +where I lay upon the sofa and tried to interest myself in a +yellow-backed novel. The puny plot of the story was so thin, +however, when compared to the deep mystery through which we were +groping, and I found my attention wander so continually from the +action to the fact, that I at last flung it across the room and +gave myself up entirely to a consideration of the events of the +day. Supposing that this unhappy young man's story were +absolutely true, then what hellish thing, what absolutely +unforeseen and extraordinary calamity could have occurred between +the time when he parted from his father, and the moment when, +drawn back by his screams, he rushed into the glade? It was +something terrible and deadly. What could it be? Might not the +nature of the injuries reveal something to my medical instincts? +I rang the bell and called for the weekly county paper, which +contained a verbatim account of the inquest. In the surgeon's +deposition it was stated that the posterior third of the left +parietal bone and the left half of the occipital bone had been +shattered by a heavy blow from a blunt weapon. I marked the spot +upon my own head. Clearly such a blow must have been struck from +behind. That was to some extent in favour of the accused, as when +seen quarrelling he was face to face with his father. Still, it +did not go for very much, for the older man might have turned his +back before the blow fell. Still, it might be worth while to call +Holmes' attention to it. Then there was the peculiar dying +reference to a rat. What could that mean? It could not be +delirium. A man dying from a sudden blow does not commonly become +delirious. No, it was more likely to be an attempt to explain how +he met his fate. But what could it indicate? I cudgelled my +brains to find some possible explanation. And then the incident +of the grey cloth seen by young McCarthy. If that were true the +murderer must have dropped some part of his dress, presumably his +overcoat, in his flight, and must have had the hardihood to +return and to carry it away at the instant when the son was +kneeling with his back turned not a dozen paces off. What a +tissue of mysteries and improbabilities the whole thing was! I +did not wonder at Lestrade's opinion, and yet I had so much faith +in Sherlock Holmes' insight that I could not lose hope as long +as every fresh fact seemed to strengthen his conviction of young +McCarthy's innocence. + +It was late before Sherlock Holmes returned. He came back alone, +for Lestrade was staying in lodgings in the town. + +"The glass still keeps very high," he remarked as he sat down. +"It is of importance that it should not rain before we are able +to go over the ground. On the other hand, a man should be at his +very best and keenest for such nice work as that, and I did not +wish to do it when fagged by a long journey. I have seen young +McCarthy." + +"And what did you learn from him?" + +"Nothing." + +"Could he throw no light?" + +"None at all. I was inclined to think at one time that he knew +who had done it and was screening him or her, but I am convinced +now that he is as puzzled as everyone else. He is not a very +quick-witted youth, though comely to look at and, I should think, +sound at heart." + +"I cannot admire his taste," I remarked, "if it is indeed a fact +that he was averse to a marriage with so charming a young lady as +this Miss Turner." + +"Ah, thereby hangs a rather painful tale. This fellow is madly, +insanely, in love with her, but some two years ago, when he was +only a lad, and before he really knew her, for she had been away +five years at a boarding-school, what does the idiot do but get +into the clutches of a barmaid in Bristol and marry her at a +registry office? No one knows a word of the matter, but you can +imagine how maddening it must be to him to be upbraided for not +doing what he would give his very eyes to do, but what he knows +to be absolutely impossible. It was sheer frenzy of this sort +which made him throw his hands up into the air when his father, +at their last interview, was goading him on to propose to Miss +Turner. On the other hand, he had no means of supporting himself, +and his father, who was by all accounts a very hard man, would +have thrown him over utterly had he known the truth. It was with +his barmaid wife that he had spent the last three days in +Bristol, and his father did not know where he was. Mark that +point. It is of importance. Good has come out of evil, however, +for the barmaid, finding from the papers that he is in serious +trouble and likely to be hanged, has thrown him over utterly and +has written to him to say that she has a husband already in the +Bermuda Dockyard, so that there is really no tie between them. I +think that that bit of news has consoled young McCarthy for all +that he has suffered." + +"But if he is innocent, who has done it?" + +"Ah! who? I would call your attention very particularly to two +points. One is that the murdered man had an appointment with +someone at the pool, and that the someone could not have been his +son, for his son was away, and he did not know when he would +return. The second is that the murdered man was heard to cry +'Cooee!' before he knew that his son had returned. Those are the +crucial points upon which the case depends. And now let us talk +about George Meredith, if you please, and we shall leave all +minor matters until to-morrow." + +There was no rain, as Holmes had foretold, and the morning broke +bright and cloudless. At nine o'clock Lestrade called for us with +the carriage, and we set off for Hatherley Farm and the Boscombe +Pool. + +"There is serious news this morning," Lestrade observed. "It is +said that Mr. Turner, of the Hall, is so ill that his life is +despaired of." + +"An elderly man, I presume?" said Holmes. + +"About sixty; but his constitution has been shattered by his life +abroad, and he has been in failing health for some time. This +business has had a very bad effect upon him. He was an old friend +of McCarthy's, and, I may add, a great benefactor to him, for I +have learned that he gave him Hatherley Farm rent free." + +"Indeed! That is interesting," said Holmes. + +"Oh, yes! In a hundred other ways he has helped him. Everybody +about here speaks of his kindness to him." + +"Really! Does it not strike you as a little singular that this +McCarthy, who appears to have had little of his own, and to have +been under such obligations to Turner, should still talk of +marrying his son to Turner's daughter, who is, presumably, +heiress to the estate, and that in such a very cocksure manner, +as if it were merely a case of a proposal and all else would +follow? It is the more strange, since we know that Turner himself +was averse to the idea. The daughter told us as much. Do you not +deduce something from that?" + +"We have got to the deductions and the inferences," said +Lestrade, winking at me. "I find it hard enough to tackle facts, +Holmes, without flying away after theories and fancies." + +"You are right," said Holmes demurely; "you do find it very hard +to tackle the facts." + +"Anyhow, I have grasped one fact which you seem to find it +difficult to get hold of," replied Lestrade with some warmth. + +"And that is--" + +"That McCarthy senior met his death from McCarthy junior and that +all theories to the contrary are the merest moonshine." + +"Well, moonshine is a brighter thing than fog," said Holmes, +laughing. "But I am very much mistaken if this is not Hatherley +Farm upon the left." + +"Yes, that is it." It was a widespread, comfortable-looking +building, two-storied, slate-roofed, with great yellow blotches +of lichen upon the grey walls. The drawn blinds and the smokeless +chimneys, however, gave it a stricken look, as though the weight +of this horror still lay heavy upon it. We called at the door, +when the maid, at Holmes' request, showed us the boots which her +master wore at the time of his death, and also a pair of the +son's, though not the pair which he had then had. Having measured +these very carefully from seven or eight different points, Holmes +desired to be led to the court-yard, from which we all followed +the winding track which led to Boscombe Pool. + +Sherlock Holmes was transformed when he was hot upon such a scent +as this. Men who had only known the quiet thinker and logician of +Baker Street would have failed to recognise him. His face flushed +and darkened. His brows were drawn into two hard black lines, +while his eyes shone out from beneath them with a steely glitter. +His face was bent downward, his shoulders bowed, his lips +compressed, and the veins stood out like whipcord in his long, +sinewy neck. His nostrils seemed to dilate with a purely animal +lust for the chase, and his mind was so absolutely concentrated +upon the matter before him that a question or remark fell +unheeded upon his ears, or, at the most, only provoked a quick, +impatient snarl in reply. Swiftly and silently he made his way +along the track which ran through the meadows, and so by way of +the woods to the Boscombe Pool. It was damp, marshy ground, as is +all that district, and there were marks of many feet, both upon +the path and amid the short grass which bounded it on either +side. Sometimes Holmes would hurry on, sometimes stop dead, and +once he made quite a little detour into the meadow. Lestrade and +I walked behind him, the detective indifferent and contemptuous, +while I watched my friend with the interest which sprang from the +conviction that every one of his actions was directed towards a +definite end. + +The Boscombe Pool, which is a little reed-girt sheet of water +some fifty yards across, is situated at the boundary between the +Hatherley Farm and the private park of the wealthy Mr. Turner. +Above the woods which lined it upon the farther side we could see +the red, jutting pinnacles which marked the site of the rich +landowner's dwelling. On the Hatherley side of the pool the woods +grew very thick, and there was a narrow belt of sodden grass +twenty paces across between the edge of the trees and the reeds +which lined the lake. Lestrade showed us the exact spot at which +the body had been found, and, indeed, so moist was the ground, +that I could plainly see the traces which had been left by the +fall of the stricken man. To Holmes, as I could see by his eager +face and peering eyes, very many other things were to be read +upon the trampled grass. He ran round, like a dog who is picking +up a scent, and then turned upon my companion. + +"What did you go into the pool for?" he asked. + +"I fished about with a rake. I thought there might be some weapon +or other trace. But how on earth--" + +"Oh, tut, tut! I have no time! That left foot of yours with its +inward twist is all over the place. A mole could trace it, and +there it vanishes among the reeds. Oh, how simple it would all +have been had I been here before they came like a herd of buffalo +and wallowed all over it. Here is where the party with the +lodge-keeper came, and they have covered all tracks for six or +eight feet round the body. But here are three separate tracks of +the same feet." He drew out a lens and lay down upon his +waterproof to have a better view, talking all the time rather to +himself than to us. "These are young McCarthy's feet. Twice he +was walking, and once he ran swiftly, so that the soles are +deeply marked and the heels hardly visible. That bears out his +story. He ran when he saw his father on the ground. Then here are +the father's feet as he paced up and down. What is this, then? It +is the butt-end of the gun as the son stood listening. And this? +Ha, ha! What have we here? Tiptoes! tiptoes! Square, too, quite +unusual boots! They come, they go, they come again--of course +that was for the cloak. Now where did they come from?" He ran up +and down, sometimes losing, sometimes finding the track until we +were well within the edge of the wood and under the shadow of a +great beech, the largest tree in the neighbourhood. Holmes traced +his way to the farther side of this and lay down once more upon +his face with a little cry of satisfaction. For a long time he +remained there, turning over the leaves and dried sticks, +gathering up what seemed to me to be dust into an envelope and +examining with his lens not only the ground but even the bark of +the tree as far as he could reach. A jagged stone was lying among +the moss, and this also he carefully examined and retained. Then +he followed a pathway through the wood until he came to the +highroad, where all traces were lost. + +"It has been a case of considerable interest," he remarked, +returning to his natural manner. "I fancy that this grey house on +the right must be the lodge. I think that I will go in and have a +word with Moran, and perhaps write a little note. Having done +that, we may drive back to our luncheon. You may walk to the cab, +and I shall be with you presently." + +It was about ten minutes before we regained our cab and drove +back into Ross, Holmes still carrying with him the stone which he +had picked up in the wood. + +"This may interest you, Lestrade," he remarked, holding it out. +"The murder was done with it." + +"I see no marks." + +"There are none." + +"How do you know, then?" + +"The grass was growing under it. It had only lain there a few +days. There was no sign of a place whence it had been taken. It +corresponds with the injuries. There is no sign of any other +weapon." + +"And the murderer?" + +"Is a tall man, left-handed, limps with the right leg, wears +thick-soled shooting-boots and a grey cloak, smokes Indian +cigars, uses a cigar-holder, and carries a blunt pen-knife in his +pocket. There are several other indications, but these may be +enough to aid us in our search." + +Lestrade laughed. "I am afraid that I am still a sceptic," he +said. "Theories are all very well, but we have to deal with a +hard-headed British jury." + +"Nous verrons," answered Holmes calmly. "You work your own +method, and I shall work mine. I shall be busy this afternoon, +and shall probably return to London by the evening train." + +"And leave your case unfinished?" + +"No, finished." + +"But the mystery?" + +"It is solved." + +"Who was the criminal, then?" + +"The gentleman I describe." + +"But who is he?" + +"Surely it would not be difficult to find out. This is not such a +populous neighbourhood." + +Lestrade shrugged his shoulders. "I am a practical man," he said, +"and I really cannot undertake to go about the country looking +for a left-handed gentleman with a game leg. I should become the +laughing-stock of Scotland Yard." + +"All right," said Holmes quietly. "I have given you the chance. +Here are your lodgings. Good-bye. I shall drop you a line before +I leave." + +Having left Lestrade at his rooms, we drove to our hotel, where +we found lunch upon the table. Holmes was silent and buried in +thought with a pained expression upon his face, as one who finds +himself in a perplexing position. + +"Look here, Watson," he said when the cloth was cleared "just sit +down in this chair and let me preach to you for a little. I don't +know quite what to do, and I should value your advice. Light a +cigar and let me expound." + + "Pray do so." + +"Well, now, in considering this case there are two points about +young McCarthy's narrative which struck us both instantly, +although they impressed me in his favour and you against him. One +was the fact that his father should, according to his account, +cry 'Cooee!' before seeing him. The other was his singular dying +reference to a rat. He mumbled several words, you understand, but +that was all that caught the son's ear. Now from this double +point our research must commence, and we will begin it by +presuming that what the lad says is absolutely true." + +"What of this 'Cooee!' then?" + +"Well, obviously it could not have been meant for the son. The +son, as far as he knew, was in Bristol. It was mere chance that +he was within earshot. The 'Cooee!' was meant to attract the +attention of whoever it was that he had the appointment with. But +'Cooee' is a distinctly Australian cry, and one which is used +between Australians. There is a strong presumption that the +person whom McCarthy expected to meet him at Boscombe Pool was +someone who had been in Australia." + +"What of the rat, then?" + +Sherlock Holmes took a folded paper from his pocket and flattened +it out on the table. "This is a map of the Colony of Victoria," +he said. "I wired to Bristol for it last night." He put his hand +over part of the map. "What do you read?" + +"ARAT," I read. + +"And now?" He raised his hand. + +"BALLARAT." + +"Quite so. That was the word the man uttered, and of which his +son only caught the last two syllables. He was trying to utter +the name of his murderer. So and so, of Ballarat." + +"It is wonderful!" I exclaimed. + +"It is obvious. And now, you see, I had narrowed the field down +considerably. The possession of a grey garment was a third point +which, granting the son's statement to be correct, was a +certainty. We have come now out of mere vagueness to the definite +conception of an Australian from Ballarat with a grey cloak." + +"Certainly." + +"And one who was at home in the district, for the pool can only +be approached by the farm or by the estate, where strangers could +hardly wander." + +"Quite so." + +"Then comes our expedition of to-day. By an examination of the +ground I gained the trifling details which I gave to that +imbecile Lestrade, as to the personality of the criminal." + +"But how did you gain them?" + +"You know my method. It is founded upon the observation of +trifles." + +"His height I know that you might roughly judge from the length +of his stride. His boots, too, might be told from their traces." + +"Yes, they were peculiar boots." + +"But his lameness?" + +"The impression of his right foot was always less distinct than +his left. He put less weight upon it. Why? Because he limped--he +was lame." + +"But his left-handedness." + +"You were yourself struck by the nature of the injury as recorded +by the surgeon at the inquest. The blow was struck from +immediately behind, and yet was upon the left side. Now, how can +that be unless it were by a left-handed man? He had stood behind +that tree during the interview between the father and son. He had +even smoked there. I found the ash of a cigar, which my special +knowledge of tobacco ashes enables me to pronounce as an Indian +cigar. I have, as you know, devoted some attention to this, and +written a little monograph on the ashes of 140 different +varieties of pipe, cigar, and cigarette tobacco. Having found the +ash, I then looked round and discovered the stump among the moss +where he had tossed it. It was an Indian cigar, of the variety +which are rolled in Rotterdam." + +"And the cigar-holder?" + +"I could see that the end had not been in his mouth. Therefore he +used a holder. The tip had been cut off, not bitten off, but the +cut was not a clean one, so I deduced a blunt pen-knife." + +"Holmes," I said, "you have drawn a net round this man from which +he cannot escape, and you have saved an innocent human life as +truly as if you had cut the cord which was hanging him. I see the +direction in which all this points. The culprit is--" + +"Mr. John Turner," cried the hotel waiter, opening the door of +our sitting-room, and ushering in a visitor. + +The man who entered was a strange and impressive figure. His +slow, limping step and bowed shoulders gave the appearance of +decrepitude, and yet his hard, deep-lined, craggy features, and +his enormous limbs showed that he was possessed of unusual +strength of body and of character. His tangled beard, grizzled +hair, and outstanding, drooping eyebrows combined to give an air +of dignity and power to his appearance, but his face was of an +ashen white, while his lips and the corners of his nostrils were +tinged with a shade of blue. It was clear to me at a glance that +he was in the grip of some deadly and chronic disease. + +"Pray sit down on the sofa," said Holmes gently. "You had my +note?" + +"Yes, the lodge-keeper brought it up. You said that you wished to +see me here to avoid scandal." + +"I thought people would talk if I went to the Hall." + +"And why did you wish to see me?" He looked across at my +companion with despair in his weary eyes, as though his question +was already answered. + +"Yes," said Holmes, answering the look rather than the words. "It +is so. I know all about McCarthy." + +The old man sank his face in his hands. "God help me!" he cried. +"But I would not have let the young man come to harm. I give you +my word that I would have spoken out if it went against him at +the Assizes." + +"I am glad to hear you say so," said Holmes gravely. + +"I would have spoken now had it not been for my dear girl. It +would break her heart--it will break her heart when she hears +that I am arrested." + +"It may not come to that," said Holmes. + +"What?" + +"I am no official agent. I understand that it was your daughter +who required my presence here, and I am acting in her interests. +Young McCarthy must be got off, however." + +"I am a dying man," said old Turner. "I have had diabetes for +years. My doctor says it is a question whether I shall live a +month. Yet I would rather die under my own roof than in a gaol." + +Holmes rose and sat down at the table with his pen in his hand +and a bundle of paper before him. "Just tell us the truth," he +said. "I shall jot down the facts. You will sign it, and Watson +here can witness it. Then I could produce your confession at the +last extremity to save young McCarthy. I promise you that I shall +not use it unless it is absolutely needed." + +"It's as well," said the old man; "it's a question whether I +shall live to the Assizes, so it matters little to me, but I +should wish to spare Alice the shock. And now I will make the +thing clear to you; it has been a long time in the acting, but +will not take me long to tell. + +"You didn't know this dead man, McCarthy. He was a devil +incarnate. I tell you that. God keep you out of the clutches of +such a man as he. His grip has been upon me these twenty years, +and he has blasted my life. I'll tell you first how I came to be +in his power. + +"It was in the early '60's at the diggings. I was a young chap +then, hot-blooded and reckless, ready to turn my hand at +anything; I got among bad companions, took to drink, had no luck +with my claim, took to the bush, and in a word became what you +would call over here a highway robber. There were six of us, and +we had a wild, free life of it, sticking up a station from time +to time, or stopping the wagons on the road to the diggings. +Black Jack of Ballarat was the name I went under, and our party +is still remembered in the colony as the Ballarat Gang. + +"One day a gold convoy came down from Ballarat to Melbourne, and +we lay in wait for it and attacked it. There were six troopers +and six of us, so it was a close thing, but we emptied four of +their saddles at the first volley. Three of our boys were killed, +however, before we got the swag. I put my pistol to the head of +the wagon-driver, who was this very man McCarthy. I wish to the +Lord that I had shot him then, but I spared him, though I saw his +wicked little eyes fixed on my face, as though to remember every +feature. We got away with the gold, became wealthy men, and made +our way over to England without being suspected. There I parted +from my old pals and determined to settle down to a quiet and +respectable life. I bought this estate, which chanced to be in +the market, and I set myself to do a little good with my money, +to make up for the way in which I had earned it. I married, too, +and though my wife died young she left me my dear little Alice. +Even when she was just a baby her wee hand seemed to lead me down +the right path as nothing else had ever done. In a word, I turned +over a new leaf and did my best to make up for the past. All was +going well when McCarthy laid his grip upon me. + +"I had gone up to town about an investment, and I met him in +Regent Street with hardly a coat to his back or a boot to his +foot. + +"'Here we are, Jack,' says he, touching me on the arm; 'we'll be +as good as a family to you. There's two of us, me and my son, and +you can have the keeping of us. If you don't--it's a fine, +law-abiding country is England, and there's always a policeman +within hail.' + +"Well, down they came to the west country, there was no shaking +them off, and there they have lived rent free on my best land +ever since. There was no rest for me, no peace, no forgetfulness; +turn where I would, there was his cunning, grinning face at my +elbow. It grew worse as Alice grew up, for he soon saw I was more +afraid of her knowing my past than of the police. Whatever he +wanted he must have, and whatever it was I gave him without +question, land, money, houses, until at last he asked a thing +which I could not give. He asked for Alice. + +"His son, you see, had grown up, and so had my girl, and as I was +known to be in weak health, it seemed a fine stroke to him that +his lad should step into the whole property. But there I was +firm. I would not have his cursed stock mixed with mine; not that +I had any dislike to the lad, but his blood was in him, and that +was enough. I stood firm. McCarthy threatened. I braved him to do +his worst. We were to meet at the pool midway between our houses +to talk it over. + +"When I went down there I found him talking with his son, so I +smoked a cigar and waited behind a tree until he should be alone. +But as I listened to his talk all that was black and bitter in +me seemed to come uppermost. He was urging his son to marry my +daughter with as little regard for what she might think as if she +were a slut from off the streets. It drove me mad to think that I +and all that I held most dear should be in the power of such a +man as this. Could I not snap the bond? I was already a dying and +a desperate man. Though clear of mind and fairly strong of limb, +I knew that my own fate was sealed. But my memory and my girl! +Both could be saved if I could but silence that foul tongue. I +did it, Mr. Holmes. I would do it again. Deeply as I have sinned, +I have led a life of martyrdom to atone for it. But that my girl +should be entangled in the same meshes which held me was more +than I could suffer. I struck him down with no more compunction +than if he had been some foul and venomous beast. His cry brought +back his son; but I had gained the cover of the wood, though I +was forced to go back to fetch the cloak which I had dropped in +my flight. That is the true story, gentlemen, of all that +occurred." + +"Well, it is not for me to judge you," said Holmes as the old man +signed the statement which had been drawn out. "I pray that we +may never be exposed to such a temptation." + +"I pray not, sir. And what do you intend to do?" + +"In view of your health, nothing. You are yourself aware that you +will soon have to answer for your deed at a higher court than the +Assizes. I will keep your confession, and if McCarthy is +condemned I shall be forced to use it. If not, it shall never be +seen by mortal eye; and your secret, whether you be alive or +dead, shall be safe with us." + +"Farewell, then," said the old man solemnly. "Your own deathbeds, +when they come, will be the easier for the thought of the peace +which you have given to mine." Tottering and shaking in all his +giant frame, he stumbled slowly from the room. + +"God help us!" said Holmes after a long silence. "Why does fate +play such tricks with poor, helpless worms? I never hear of such +a case as this that I do not think of Baxter's words, and say, +'There, but for the grace of God, goes Sherlock Holmes.'" + +James McCarthy was acquitted at the Assizes on the strength of a +number of objections which had been drawn out by Holmes and +submitted to the defending counsel. Old Turner lived for seven +months after our interview, but he is now dead; and there is +every prospect that the son and daughter may come to live happily +together in ignorance of the black cloud which rests upon their +past. + + + +ADVENTURE V. THE FIVE ORANGE PIPS + +When I glance over my notes and records of the Sherlock Holmes +cases between the years '82 and '90, I am faced by so many which +present strange and interesting features that it is no easy +matter to know which to choose and which to leave. Some, however, +have already gained publicity through the papers, and others have +not offered a field for those peculiar qualities which my friend +possessed in so high a degree, and which it is the object of +these papers to illustrate. Some, too, have baffled his +analytical skill, and would be, as narratives, beginnings without +an ending, while others have been but partially cleared up, and +have their explanations founded rather upon conjecture and +surmise than on that absolute logical proof which was so dear to +him. There is, however, one of these last which was so remarkable +in its details and so startling in its results that I am tempted +to give some account of it in spite of the fact that there are +points in connection with it which never have been, and probably +never will be, entirely cleared up. + +The year '87 furnished us with a long series of cases of greater +or less interest, of which I retain the records. Among my +headings under this one twelve months I find an account of the +adventure of the Paradol Chamber, of the Amateur Mendicant +Society, who held a luxurious club in the lower vault of a +furniture warehouse, of the facts connected with the loss of the +British barque "Sophy Anderson", of the singular adventures of the +Grice Patersons in the island of Uffa, and finally of the +Camberwell poisoning case. In the latter, as may be remembered, +Sherlock Holmes was able, by winding up the dead man's watch, to +prove that it had been wound up two hours before, and that +therefore the deceased had gone to bed within that time--a +deduction which was of the greatest importance in clearing up the +case. All these I may sketch out at some future date, but none of +them present such singular features as the strange train of +circumstances which I have now taken up my pen to describe. + +It was in the latter days of September, and the equinoctial gales +had set in with exceptional violence. All day the wind had +screamed and the rain had beaten against the windows, so that +even here in the heart of great, hand-made London we were forced +to raise our minds for the instant from the routine of life and +to recognise the presence of those great elemental forces which +shriek at mankind through the bars of his civilisation, like +untamed beasts in a cage. As evening drew in, the storm grew +higher and louder, and the wind cried and sobbed like a child in +the chimney. Sherlock Holmes sat moodily at one side of the +fireplace cross-indexing his records of crime, while I at the +other was deep in one of Clark Russell's fine sea-stories until +the howl of the gale from without seemed to blend with the text, +and the splash of the rain to lengthen out into the long swash of +the sea waves. My wife was on a visit to her mother's, and for a +few days I was a dweller once more in my old quarters at Baker +Street. + +"Why," said I, glancing up at my companion, "that was surely the +bell. Who could come to-night? Some friend of yours, perhaps?" + +"Except yourself I have none," he answered. "I do not encourage +visitors." + +"A client, then?" + +"If so, it is a serious case. Nothing less would bring a man out +on such a day and at such an hour. But I take it that it is more +likely to be some crony of the landlady's." + +Sherlock Holmes was wrong in his conjecture, however, for there +came a step in the passage and a tapping at the door. He +stretched out his long arm to turn the lamp away from himself and +towards the vacant chair upon which a newcomer must sit. + +"Come in!" said he. + +The man who entered was young, some two-and-twenty at the +outside, well-groomed and trimly clad, with something of +refinement and delicacy in his bearing. The streaming umbrella +which he held in his hand, and his long shining waterproof told +of the fierce weather through which he had come. He looked about +him anxiously in the glare of the lamp, and I could see that his +face was pale and his eyes heavy, like those of a man who is +weighed down with some great anxiety. + +"I owe you an apology," he said, raising his golden pince-nez to +his eyes. "I trust that I am not intruding. I fear that I have +brought some traces of the storm and rain into your snug +chamber." + +"Give me your coat and umbrella," said Holmes. "They may rest +here on the hook and will be dry presently. You have come up from +the south-west, I see." + +"Yes, from Horsham." + +"That clay and chalk mixture which I see upon your toe caps is +quite distinctive." + +"I have come for advice." + +"That is easily got." + +"And help." + +"That is not always so easy." + +"I have heard of you, Mr. Holmes. I heard from Major Prendergast +how you saved him in the Tankerville Club scandal." + +"Ah, of course. He was wrongfully accused of cheating at cards." + +"He said that you could solve anything." + +"He said too much." + +"That you are never beaten." + +"I have been beaten four times--three times by men, and once by a +woman." + +"But what is that compared with the number of your successes?" + +"It is true that I have been generally successful." + +"Then you may be so with me." + +"I beg that you will draw your chair up to the fire and favour me +with some details as to your case." + +"It is no ordinary one." + +"None of those which come to me are. I am the last court of +appeal." + +"And yet I question, sir, whether, in all your experience, you +have ever listened to a more mysterious and inexplicable chain of +events than those which have happened in my own family." + +"You fill me with interest," said Holmes. "Pray give us the +essential facts from the commencement, and I can afterwards +question you as to those details which seem to me to be most +important." + +The young man pulled his chair up and pushed his wet feet out +towards the blaze. + +"My name," said he, "is John Openshaw, but my own affairs have, +as far as I can understand, little to do with this awful +business. It is a hereditary matter; so in order to give you an +idea of the facts, I must go back to the commencement of the +affair. + +"You must know that my grandfather had two sons--my uncle Elias +and my father Joseph. My father had a small factory at Coventry, +which he enlarged at the time of the invention of bicycling. He +was a patentee of the Openshaw unbreakable tire, and his business +met with such success that he was able to sell it and to retire +upon a handsome competence. + +"My uncle Elias emigrated to America when he was a young man and +became a planter in Florida, where he was reported to have done +very well. At the time of the war he fought in Jackson's army, +and afterwards under Hood, where he rose to be a colonel. When +Lee laid down his arms my uncle returned to his plantation, where +he remained for three or four years. About 1869 or 1870 he came +back to Europe and took a small estate in Sussex, near Horsham. +He had made a very considerable fortune in the States, and his +reason for leaving them was his aversion to the negroes, and his +dislike of the Republican policy in extending the franchise to +them. He was a singular man, fierce and quick-tempered, very +foul-mouthed when he was angry, and of a most retiring +disposition. During all the years that he lived at Horsham, I +doubt if ever he set foot in the town. He had a garden and two or +three fields round his house, and there he would take his +exercise, though very often for weeks on end he would never leave +his room. He drank a great deal of brandy and smoked very +heavily, but he would see no society and did not want any +friends, not even his own brother. + +"He didn't mind me; in fact, he took a fancy to me, for at the +time when he saw me first I was a youngster of twelve or so. This +would be in the year 1878, after he had been eight or nine years +in England. He begged my father to let me live with him and he +was very kind to me in his way. When he was sober he used to be +fond of playing backgammon and draughts with me, and he would +make me his representative both with the servants and with the +tradespeople, so that by the time that I was sixteen I was quite +master of the house. I kept all the keys and could go where I +liked and do what I liked, so long as I did not disturb him in +his privacy. There was one singular exception, however, for he +had a single room, a lumber-room up among the attics, which was +invariably locked, and which he would never permit either me or +anyone else to enter. With a boy's curiosity I have peeped +through the keyhole, but I was never able to see more than such a +collection of old trunks and bundles as would be expected in such +a room. + +"One day--it was in March, 1883--a letter with a foreign stamp +lay upon the table in front of the colonel's plate. It was not a +common thing for him to receive letters, for his bills were all +paid in ready money, and he had no friends of any sort. 'From +India!' said he as he took it up, 'Pondicherry postmark! What can +this be?' Opening it hurriedly, out there jumped five little +dried orange pips, which pattered down upon his plate. I began to +laugh at this, but the laugh was struck from my lips at the sight +of his face. His lip had fallen, his eyes were protruding, his +skin the colour of putty, and he glared at the envelope which he +still held in his trembling hand, 'K. K. K.!' he shrieked, and +then, 'My God, my God, my sins have overtaken me!' + +"'What is it, uncle?' I cried. + +"'Death,' said he, and rising from the table he retired to his +room, leaving me palpitating with horror. I took up the envelope +and saw scrawled in red ink upon the inner flap, just above the +gum, the letter K three times repeated. There was nothing else +save the five dried pips. What could be the reason of his +overpowering terror? I left the breakfast-table, and as I +ascended the stair I met him coming down with an old rusty key, +which must have belonged to the attic, in one hand, and a small +brass box, like a cashbox, in the other. + +"'They may do what they like, but I'll checkmate them still,' +said he with an oath. 'Tell Mary that I shall want a fire in my +room to-day, and send down to Fordham, the Horsham lawyer.' + +"I did as he ordered, and when the lawyer arrived I was asked to +step up to the room. The fire was burning brightly, and in the +grate there was a mass of black, fluffy ashes, as of burned +paper, while the brass box stood open and empty beside it. As I +glanced at the box I noticed, with a start, that upon the lid was +printed the treble K which I had read in the morning upon the +envelope. + +"'I wish you, John,' said my uncle, 'to witness my will. I leave +my estate, with all its advantages and all its disadvantages, to +my brother, your father, whence it will, no doubt, descend to +you. If you can enjoy it in peace, well and good! If you find you +cannot, take my advice, my boy, and leave it to your deadliest +enemy. I am sorry to give you such a two-edged thing, but I can't +say what turn things are going to take. Kindly sign the paper +where Mr. Fordham shows you.' + +"I signed the paper as directed, and the lawyer took it away with +him. The singular incident made, as you may think, the deepest +impression upon me, and I pondered over it and turned it every +way in my mind without being able to make anything of it. Yet I +could not shake off the vague feeling of dread which it left +behind, though the sensation grew less keen as the weeks passed +and nothing happened to disturb the usual routine of our lives. I +could see a change in my uncle, however. He drank more than ever, +and he was less inclined for any sort of society. Most of his +time he would spend in his room, with the door locked upon the +inside, but sometimes he would emerge in a sort of drunken frenzy +and would burst out of the house and tear about the garden with a +revolver in his hand, screaming out that he was afraid of no man, +and that he was not to be cooped up, like a sheep in a pen, by +man or devil. When these hot fits were over, however, he would +rush tumultuously in at the door and lock and bar it behind him, +like a man who can brazen it out no longer against the terror +which lies at the roots of his soul. At such times I have seen +his face, even on a cold day, glisten with moisture, as though it +were new raised from a basin. + +"Well, to come to an end of the matter, Mr. Holmes, and not to +abuse your patience, there came a night when he made one of those +drunken sallies from which he never came back. We found him, when +we went to search for him, face downward in a little +green-scummed pool, which lay at the foot of the garden. There +was no sign of any violence, and the water was but two feet deep, +so that the jury, having regard to his known eccentricity, +brought in a verdict of 'suicide.' But I, who knew how he winced +from the very thought of death, had much ado to persuade myself +that he had gone out of his way to meet it. The matter passed, +however, and my father entered into possession of the estate, and +of some 14,000 pounds, which lay to his credit at the bank." + +"One moment," Holmes interposed, "your statement is, I foresee, +one of the most remarkable to which I have ever listened. Let me +have the date of the reception by your uncle of the letter, and +the date of his supposed suicide." + +"The letter arrived on March 10, 1883. His death was seven weeks +later, upon the night of May 2nd." + +"Thank you. Pray proceed." + +"When my father took over the Horsham property, he, at my +request, made a careful examination of the attic, which had been +always locked up. We found the brass box there, although its +contents had been destroyed. On the inside of the cover was a +paper label, with the initials of K. K. K. repeated upon it, and +'Letters, memoranda, receipts, and a register' written beneath. +These, we presume, indicated the nature of the papers which had +been destroyed by Colonel Openshaw. For the rest, there was +nothing of much importance in the attic save a great many +scattered papers and note-books bearing upon my uncle's life in +America. Some of them were of the war time and showed that he had +done his duty well and had borne the repute of a brave soldier. +Others were of a date during the reconstruction of the Southern +states, and were mostly concerned with politics, for he had +evidently taken a strong part in opposing the carpet-bag +politicians who had been sent down from the North. + +"Well, it was the beginning of '84 when my father came to live at +Horsham, and all went as well as possible with us until the +January of '85. On the fourth day after the new year I heard my +father give a sharp cry of surprise as we sat together at the +breakfast-table. There he was, sitting with a newly opened +envelope in one hand and five dried orange pips in the +outstretched palm of the other one. He had always laughed at what +he called my cock-and-bull story about the colonel, but he looked +very scared and puzzled now that the same thing had come upon +himself. + +"'Why, what on earth does this mean, John?' he stammered. + +"My heart had turned to lead. 'It is K. K. K.,' said I. + +"He looked inside the envelope. 'So it is,' he cried. 'Here are +the very letters. But what is this written above them?' + +"'Put the papers on the sundial,' I read, peeping over his +shoulder. + +"'What papers? What sundial?' he asked. + +"'The sundial in the garden. There is no other,' said I; 'but the +papers must be those that are destroyed.' + +"'Pooh!' said he, gripping hard at his courage. 'We are in a +civilised land here, and we can't have tomfoolery of this kind. +Where does the thing come from?' + +"'From Dundee,' I answered, glancing at the postmark. + +"'Some preposterous practical joke,' said he. 'What have I to do +with sundials and papers? I shall take no notice of such +nonsense.' + +"'I should certainly speak to the police,' I said. + +"'And be laughed at for my pains. Nothing of the sort.' + +"'Then let me do so?' + +"'No, I forbid you. I won't have a fuss made about such +nonsense.' + +"It was in vain to argue with him, for he was a very obstinate +man. I went about, however, with a heart which was full of +forebodings. + +"On the third day after the coming of the letter my father went +from home to visit an old friend of his, Major Freebody, who is +in command of one of the forts upon Portsdown Hill. I was glad +that he should go, for it seemed to me that he was farther from +danger when he was away from home. In that, however, I was in +error. Upon the second day of his absence I received a telegram +from the major, imploring me to come at once. My father had +fallen over one of the deep chalk-pits which abound in the +neighbourhood, and was lying senseless, with a shattered skull. I +hurried to him, but he passed away without having ever recovered +his consciousness. He had, as it appears, been returning from +Fareham in the twilight, and as the country was unknown to him, +and the chalk-pit unfenced, the jury had no hesitation in +bringing in a verdict of 'death from accidental causes.' +Carefully as I examined every fact connected with his death, I +was unable to find anything which could suggest the idea of +murder. There were no signs of violence, no footmarks, no +robbery, no record of strangers having been seen upon the roads. +And yet I need not tell you that my mind was far from at ease, +and that I was well-nigh certain that some foul plot had been +woven round him. + +"In this sinister way I came into my inheritance. You will ask me +why I did not dispose of it? I answer, because I was well +convinced that our troubles were in some way dependent upon an +incident in my uncle's life, and that the danger would be as +pressing in one house as in another. + +"It was in January, '85, that my poor father met his end, and two +years and eight months have elapsed since then. During that time +I have lived happily at Horsham, and I had begun to hope that +this curse had passed away from the family, and that it had ended +with the last generation. I had begun to take comfort too soon, +however; yesterday morning the blow fell in the very shape in +which it had come upon my father." + +The young man took from his waistcoat a crumpled envelope, and +turning to the table he shook out upon it five little dried +orange pips. + +"This is the envelope," he continued. "The postmark is +London--eastern division. Within are the very words which were +upon my father's last message: 'K. K. K.'; and then 'Put the +papers on the sundial.'" + +"What have you done?" asked Holmes. + +"Nothing." + +"Nothing?" + +"To tell the truth"--he sank his face into his thin, white +hands--"I have felt helpless. I have felt like one of those poor +rabbits when the snake is writhing towards it. I seem to be in +the grasp of some resistless, inexorable evil, which no foresight +and no precautions can guard against." + +"Tut! tut!" cried Sherlock Holmes. "You must act, man, or you are +lost. Nothing but energy can save you. This is no time for +despair." + +"I have seen the police." + +"Ah!" + +"But they listened to my story with a smile. I am convinced that +the inspector has formed the opinion that the letters are all +practical jokes, and that the deaths of my relations were really +accidents, as the jury stated, and were not to be connected with +the warnings." + +Holmes shook his clenched hands in the air. "Incredible +imbecility!" he cried. + +"They have, however, allowed me a policeman, who may remain in +the house with me." + +"Has he come with you to-night?" + +"No. His orders were to stay in the house." + +Again Holmes raved in the air. + +"Why did you come to me," he cried, "and, above all, why did you +not come at once?" + +"I did not know. It was only to-day that I spoke to Major +Prendergast about my troubles and was advised by him to come to +you." + +"It is really two days since you had the letter. We should have +acted before this. You have no further evidence, I suppose, than +that which you have placed before us--no suggestive detail which +might help us?" + +"There is one thing," said John Openshaw. He rummaged in his coat +pocket, and, drawing out a piece of discoloured, blue-tinted +paper, he laid it out upon the table. "I have some remembrance," +said he, "that on the day when my uncle burned the papers I +observed that the small, unburned margins which lay amid the +ashes were of this particular colour. I found this single sheet +upon the floor of his room, and I am inclined to think that it +may be one of the papers which has, perhaps, fluttered out from +among the others, and in that way has escaped destruction. Beyond +the mention of pips, I do not see that it helps us much. I think +myself that it is a page from some private diary. The writing is +undoubtedly my uncle's." + +Holmes moved the lamp, and we both bent over the sheet of paper, +which showed by its ragged edge that it had indeed been torn from +a book. It was headed, "March, 1869," and beneath were the +following enigmatical notices: + +"4th. Hudson came. Same old platform. + +"7th. Set the pips on McCauley, Paramore, and + John Swain, of St. Augustine. + +"9th. McCauley cleared. + +"10th. John Swain cleared. + +"12th. Visited Paramore. All well." + +"Thank you!" said Holmes, folding up the paper and returning it +to our visitor. "And now you must on no account lose another +instant. We cannot spare time even to discuss what you have told +me. You must get home instantly and act." + +"What shall I do?" + +"There is but one thing to do. It must be done at once. You must +put this piece of paper which you have shown us into the brass +box which you have described. You must also put in a note to say +that all the other papers were burned by your uncle, and that +this is the only one which remains. You must assert that in such +words as will carry conviction with them. Having done this, you +must at once put the box out upon the sundial, as directed. Do +you understand?" + +"Entirely." + +"Do not think of revenge, or anything of the sort, at present. I +think that we may gain that by means of the law; but we have our +web to weave, while theirs is already woven. The first +consideration is to remove the pressing danger which threatens +you. The second is to clear up the mystery and to punish the +guilty parties." + +"I thank you," said the young man, rising and pulling on his +overcoat. "You have given me fresh life and hope. I shall +certainly do as you advise." + +"Do not lose an instant. And, above all, take care of yourself in +the meanwhile, for I do not think that there can be a doubt that +you are threatened by a very real and imminent danger. How do you +go back?" + +"By train from Waterloo." + +"It is not yet nine. The streets will be crowded, so I trust that +you may be in safety. And yet you cannot guard yourself too +closely." + +"I am armed." + +"That is well. To-morrow I shall set to work upon your case." + +"I shall see you at Horsham, then?" + +"No, your secret lies in London. It is there that I shall seek +it." + +"Then I shall call upon you in a day, or in two days, with news +as to the box and the papers. I shall take your advice in every +particular." He shook hands with us and took his leave. Outside +the wind still screamed and the rain splashed and pattered +against the windows. This strange, wild story seemed to have come +to us from amid the mad elements--blown in upon us like a sheet +of sea-weed in a gale--and now to have been reabsorbed by them +once more. + +Sherlock Holmes sat for some time in silence, with his head sunk +forward and his eyes bent upon the red glow of the fire. Then he +lit his pipe, and leaning back in his chair he watched the blue +smoke-rings as they chased each other up to the ceiling. + +"I think, Watson," he remarked at last, "that of all our cases we +have had none more fantastic than this." + +"Save, perhaps, the Sign of Four." + +"Well, yes. Save, perhaps, that. And yet this John Openshaw seems +to me to be walking amid even greater perils than did the +Sholtos." + +"But have you," I asked, "formed any definite conception as to +what these perils are?" + +"There can be no question as to their nature," he answered. + +"Then what are they? Who is this K. K. K., and why does he pursue +this unhappy family?" + +Sherlock Holmes closed his eyes and placed his elbows upon the +arms of his chair, with his finger-tips together. "The ideal +reasoner," he remarked, "would, when he had once been shown a +single fact in all its bearings, deduce from it not only all the +chain of events which led up to it but also all the results which +would follow from it. As Cuvier could correctly describe a whole +animal by the contemplation of a single bone, so the observer who +has thoroughly understood one link in a series of incidents +should be able to accurately state all the other ones, both +before and after. We have not yet grasped the results which the +reason alone can attain to. Problems may be solved in the study +which have baffled all those who have sought a solution by the +aid of their senses. To carry the art, however, to its highest +pitch, it is necessary that the reasoner should be able to +utilise all the facts which have come to his knowledge; and this +in itself implies, as you will readily see, a possession of all +knowledge, which, even in these days of free education and +encyclopaedias, is a somewhat rare accomplishment. It is not so +impossible, however, that a man should possess all knowledge +which is likely to be useful to him in his work, and this I have +endeavoured in my case to do. If I remember rightly, you on one +occasion, in the early days of our friendship, defined my limits +in a very precise fashion." + +"Yes," I answered, laughing. "It was a singular document. +Philosophy, astronomy, and politics were marked at zero, I +remember. Botany variable, geology profound as regards the +mud-stains from any region within fifty miles of town, chemistry +eccentric, anatomy unsystematic, sensational literature and crime +records unique, violin-player, boxer, swordsman, lawyer, and +self-poisoner by cocaine and tobacco. Those, I think, were the +main points of my analysis." + +Holmes grinned at the last item. "Well," he said, "I say now, as +I said then, that a man should keep his little brain-attic +stocked with all the furniture that he is likely to use, and the +rest he can put away in the lumber-room of his library, where he +can get it if he wants it. Now, for such a case as the one which +has been submitted to us to-night, we need certainly to muster +all our resources. Kindly hand me down the letter K of the +'American Encyclopaedia' which stands upon the shelf beside you. +Thank you. Now let us consider the situation and see what may be +deduced from it. In the first place, we may start with a strong +presumption that Colonel Openshaw had some very strong reason for +leaving America. Men at his time of life do not change all their +habits and exchange willingly the charming climate of Florida for +the lonely life of an English provincial town. His extreme love +of solitude in England suggests the idea that he was in fear of +someone or something, so we may assume as a working hypothesis +that it was fear of someone or something which drove him from +America. As to what it was he feared, we can only deduce that by +considering the formidable letters which were received by himself +and his successors. Did you remark the postmarks of those +letters?" + +"The first was from Pondicherry, the second from Dundee, and the +third from London." + +"From East London. What do you deduce from that?" + +"They are all seaports. That the writer was on board of a ship." + +"Excellent. We have already a clue. There can be no doubt that +the probability--the strong probability--is that the writer was +on board of a ship. And now let us consider another point. In the +case of Pondicherry, seven weeks elapsed between the threat and +its fulfilment, in Dundee it was only some three or four days. +Does that suggest anything?" + +"A greater distance to travel." + +"But the letter had also a greater distance to come." + +"Then I do not see the point." + +"There is at least a presumption that the vessel in which the man +or men are is a sailing-ship. It looks as if they always send +their singular warning or token before them when starting upon +their mission. You see how quickly the deed followed the sign +when it came from Dundee. If they had come from Pondicherry in a +steamer they would have arrived almost as soon as their letter. +But, as a matter of fact, seven weeks elapsed. I think that those +seven weeks represented the difference between the mail-boat which +brought the letter and the sailing vessel which brought the +writer." + +"It is possible." + +"More than that. It is probable. And now you see the deadly +urgency of this new case, and why I urged young Openshaw to +caution. The blow has always fallen at the end of the time which +it would take the senders to travel the distance. But this one +comes from London, and therefore we cannot count upon delay." + +"Good God!" I cried. "What can it mean, this relentless +persecution?" + +"The papers which Openshaw carried are obviously of vital +importance to the person or persons in the sailing-ship. I think +that it is quite clear that there must be more than one of them. +A single man could not have carried out two deaths in such a way +as to deceive a coroner's jury. There must have been several in +it, and they must have been men of resource and determination. +Their papers they mean to have, be the holder of them who it may. +In this way you see K. K. K. ceases to be the initials of an +individual and becomes the badge of a society." + +"But of what society?" + +"Have you never--" said Sherlock Holmes, bending forward and +sinking his voice--"have you never heard of the Ku Klux Klan?" + +"I never have." + +Holmes turned over the leaves of the book upon his knee. "Here it +is," said he presently: + +"'Ku Klux Klan. A name derived from the fanciful resemblance to +the sound produced by cocking a rifle. This terrible secret +society was formed by some ex-Confederate soldiers in the +Southern states after the Civil War, and it rapidly formed local +branches in different parts of the country, notably in Tennessee, +Louisiana, the Carolinas, Georgia, and Florida. Its power was +used for political purposes, principally for the terrorising of +the negro voters and the murdering and driving from the country +of those who were opposed to its views. Its outrages were usually +preceded by a warning sent to the marked man in some fantastic +but generally recognised shape--a sprig of oak-leaves in some +parts, melon seeds or orange pips in others. On receiving this +the victim might either openly abjure his former ways, or might +fly from the country. If he braved the matter out, death would +unfailingly come upon him, and usually in some strange and +unforeseen manner. So perfect was the organisation of the +society, and so systematic its methods, that there is hardly a +case upon record where any man succeeded in braving it with +impunity, or in which any of its outrages were traced home to the +perpetrators. For some years the organisation flourished in spite +of the efforts of the United States government and of the better +classes of the community in the South. Eventually, in the year +1869, the movement rather suddenly collapsed, although there have +been sporadic outbreaks of the same sort since that date.' + +"You will observe," said Holmes, laying down the volume, "that +the sudden breaking up of the society was coincident with the +disappearance of Openshaw from America with their papers. It may +well have been cause and effect. It is no wonder that he and his +family have some of the more implacable spirits upon their track. +You can understand that this register and diary may implicate +some of the first men in the South, and that there may be many +who will not sleep easy at night until it is recovered." + +"Then the page we have seen--" + +"Is such as we might expect. It ran, if I remember right, 'sent +the pips to A, B, and C'--that is, sent the society's warning to +them. Then there are successive entries that A and B cleared, or +left the country, and finally that C was visited, with, I fear, a +sinister result for C. Well, I think, Doctor, that we may let +some light into this dark place, and I believe that the only +chance young Openshaw has in the meantime is to do what I have +told him. There is nothing more to be said or to be done +to-night, so hand me over my violin and let us try to forget for +half an hour the miserable weather and the still more miserable +ways of our fellow-men." + + +It had cleared in the morning, and the sun was shining with a +subdued brightness through the dim veil which hangs over the +great city. Sherlock Holmes was already at breakfast when I came +down. + +"You will excuse me for not waiting for you," said he; "I have, I +foresee, a very busy day before me in looking into this case of +young Openshaw's." + +"What steps will you take?" I asked. + +"It will very much depend upon the results of my first inquiries. +I may have to go down to Horsham, after all." + +"You will not go there first?" + +"No, I shall commence with the City. Just ring the bell and the +maid will bring up your coffee." + +As I waited, I lifted the unopened newspaper from the table and +glanced my eye over it. It rested upon a heading which sent a +chill to my heart. + +"Holmes," I cried, "you are too late." + +"Ah!" said he, laying down his cup, "I feared as much. How was it +done?" He spoke calmly, but I could see that he was deeply moved. + +"My eye caught the name of Openshaw, and the heading 'Tragedy +Near Waterloo Bridge.' Here is the account: + +"Between nine and ten last night Police-Constable Cook, of the H +Division, on duty near Waterloo Bridge, heard a cry for help and +a splash in the water. The night, however, was extremely dark and +stormy, so that, in spite of the help of several passers-by, it +was quite impossible to effect a rescue. The alarm, however, was +given, and, by the aid of the water-police, the body was +eventually recovered. It proved to be that of a young gentleman +whose name, as it appears from an envelope which was found in his +pocket, was John Openshaw, and whose residence is near Horsham. +It is conjectured that he may have been hurrying down to catch +the last train from Waterloo Station, and that in his haste and +the extreme darkness he missed his path and walked over the edge +of one of the small landing-places for river steamboats. The body +exhibited no traces of violence, and there can be no doubt that +the deceased had been the victim of an unfortunate accident, +which should have the effect of calling the attention of the +authorities to the condition of the riverside landing-stages." + +We sat in silence for some minutes, Holmes more depressed and +shaken than I had ever seen him. + +"That hurts my pride, Watson," he said at last. "It is a petty +feeling, no doubt, but it hurts my pride. It becomes a personal +matter with me now, and, if God sends me health, I shall set my +hand upon this gang. That he should come to me for help, and that +I should send him away to his death--!" He sprang from his chair +and paced about the room in uncontrollable agitation, with a +flush upon his sallow cheeks and a nervous clasping and +unclasping of his long thin hands. + +"They must be cunning devils," he exclaimed at last. "How could +they have decoyed him down there? The Embankment is not on the +direct line to the station. The bridge, no doubt, was too +crowded, even on such a night, for their purpose. Well, Watson, +we shall see who will win in the long run. I am going out now!" + +"To the police?" + +"No; I shall be my own police. When I have spun the web they may +take the flies, but not before." + +All day I was engaged in my professional work, and it was late in +the evening before I returned to Baker Street. Sherlock Holmes +had not come back yet. It was nearly ten o'clock before he +entered, looking pale and worn. He walked up to the sideboard, +and tearing a piece from the loaf he devoured it voraciously, +washing it down with a long draught of water. + +"You are hungry," I remarked. + +"Starving. It had escaped my memory. I have had nothing since +breakfast." + +"Nothing?" + +"Not a bite. I had no time to think of it." + +"And how have you succeeded?" + +"Well." + +"You have a clue?" + +"I have them in the hollow of my hand. Young Openshaw shall not +long remain unavenged. Why, Watson, let us put their own devilish +trade-mark upon them. It is well thought of!" + +"What do you mean?" + +He took an orange from the cupboard, and tearing it to pieces he +squeezed out the pips upon the table. Of these he took five and +thrust them into an envelope. On the inside of the flap he wrote +"S. H. for J. O." Then he sealed it and addressed it to "Captain +James Calhoun, Barque 'Lone Star,' Savannah, Georgia." + +"That will await him when he enters port," said he, chuckling. +"It may give him a sleepless night. He will find it as sure a +precursor of his fate as Openshaw did before him." + +"And who is this Captain Calhoun?" + +"The leader of the gang. I shall have the others, but he first." + +"How did you trace it, then?" + +He took a large sheet of paper from his pocket, all covered with +dates and names. + +"I have spent the whole day," said he, "over Lloyd's registers +and files of the old papers, following the future career of every +vessel which touched at Pondicherry in January and February in +'83. There were thirty-six ships of fair tonnage which were +reported there during those months. Of these, one, the 'Lone Star,' +instantly attracted my attention, since, although it was reported +as having cleared from London, the name is that which is given to +one of the states of the Union." + +"Texas, I think." + +"I was not and am not sure which; but I knew that the ship must +have an American origin." + +"What then?" + +"I searched the Dundee records, and when I found that the barque +'Lone Star' was there in January, '85, my suspicion became a +certainty. I then inquired as to the vessels which lay at present +in the port of London." + +"Yes?" + +"The 'Lone Star' had arrived here last week. I went down to the +Albert Dock and found that she had been taken down the river by +the early tide this morning, homeward bound to Savannah. I wired +to Gravesend and learned that she had passed some time ago, and +as the wind is easterly I have no doubt that she is now past the +Goodwins and not very far from the Isle of Wight." + +"What will you do, then?" + +"Oh, I have my hand upon him. He and the two mates, are as I +learn, the only native-born Americans in the ship. The others are +Finns and Germans. I know, also, that they were all three away +from the ship last night. I had it from the stevedore who has +been loading their cargo. By the time that their sailing-ship +reaches Savannah the mail-boat will have carried this letter, and +the cable will have informed the police of Savannah that these +three gentlemen are badly wanted here upon a charge of murder." + +There is ever a flaw, however, in the best laid of human plans, +and the murderers of John Openshaw were never to receive the +orange pips which would show them that another, as cunning and as +resolute as themselves, was upon their track. Very long and very +severe were the equinoctial gales that year. We waited long for +news of the "Lone Star" of Savannah, but none ever reached us. We +did at last hear that somewhere far out in the Atlantic a +shattered stern-post of a boat was seen swinging in the trough +of a wave, with the letters "L. S." carved upon it, and that is +all which we shall ever know of the fate of the "Lone Star." + + + +ADVENTURE VI. THE MAN WITH THE TWISTED LIP + +Isa Whitney, brother of the late Elias Whitney, D.D., Principal +of the Theological College of St. George's, was much addicted to +opium. The habit grew upon him, as I understand, from some +foolish freak when he was at college; for having read De +Quincey's description of his dreams and sensations, he had +drenched his tobacco with laudanum in an attempt to produce the +same effects. He found, as so many more have done, that the +practice is easier to attain than to get rid of, and for many +years he continued to be a slave to the drug, an object of +mingled horror and pity to his friends and relatives. I can see +him now, with yellow, pasty face, drooping lids, and pin-point +pupils, all huddled in a chair, the wreck and ruin of a noble +man. + +One night--it was in June, '89--there came a ring to my bell, +about the hour when a man gives his first yawn and glances at the +clock. I sat up in my chair, and my wife laid her needle-work +down in her lap and made a little face of disappointment. + +"A patient!" said she. "You'll have to go out." + +I groaned, for I was newly come back from a weary day. + +We heard the door open, a few hurried words, and then quick steps +upon the linoleum. Our own door flew open, and a lady, clad in +some dark-coloured stuff, with a black veil, entered the room. + +"You will excuse my calling so late," she began, and then, +suddenly losing her self-control, she ran forward, threw her arms +about my wife's neck, and sobbed upon her shoulder. "Oh, I'm in +such trouble!" she cried; "I do so want a little help." + +"Why," said my wife, pulling up her veil, "it is Kate Whitney. +How you startled me, Kate! I had not an idea who you were when +you came in." + +"I didn't know what to do, so I came straight to you." That was +always the way. Folk who were in grief came to my wife like birds +to a light-house. + +"It was very sweet of you to come. Now, you must have some wine +and water, and sit here comfortably and tell us all about it. Or +should you rather that I sent James off to bed?" + +"Oh, no, no! I want the doctor's advice and help, too. It's about +Isa. He has not been home for two days. I am so frightened about +him!" + +It was not the first time that she had spoken to us of her +husband's trouble, to me as a doctor, to my wife as an old friend +and school companion. We soothed and comforted her by such words +as we could find. Did she know where her husband was? Was it +possible that we could bring him back to her? + +It seems that it was. She had the surest information that of late +he had, when the fit was on him, made use of an opium den in the +farthest east of the City. Hitherto his orgies had always been +confined to one day, and he had come back, twitching and +shattered, in the evening. But now the spell had been upon him +eight-and-forty hours, and he lay there, doubtless among the +dregs of the docks, breathing in the poison or sleeping off the +effects. There he was to be found, she was sure of it, at the Bar +of Gold, in Upper Swandam Lane. But what was she to do? How could +she, a young and timid woman, make her way into such a place and +pluck her husband out from among the ruffians who surrounded him? + +There was the case, and of course there was but one way out of +it. Might I not escort her to this place? And then, as a second +thought, why should she come at all? I was Isa Whitney's medical +adviser, and as such I had influence over him. I could manage it +better if I were alone. I promised her on my word that I would +send him home in a cab within two hours if he were indeed at the +address which she had given me. And so in ten minutes I had left +my armchair and cheery sitting-room behind me, and was speeding +eastward in a hansom on a strange errand, as it seemed to me at +the time, though the future only could show how strange it was to +be. + +But there was no great difficulty in the first stage of my +adventure. Upper Swandam Lane is a vile alley lurking behind the +high wharves which line the north side of the river to the east +of London Bridge. Between a slop-shop and a gin-shop, approached +by a steep flight of steps leading down to a black gap like the +mouth of a cave, I found the den of which I was in search. +Ordering my cab to wait, I passed down the steps, worn hollow in +the centre by the ceaseless tread of drunken feet; and by the +light of a flickering oil-lamp above the door I found the latch +and made my way into a long, low room, thick and heavy with the +brown opium smoke, and terraced with wooden berths, like the +forecastle of an emigrant ship. + +Through the gloom one could dimly catch a glimpse of bodies lying +in strange fantastic poses, bowed shoulders, bent knees, heads +thrown back, and chins pointing upward, with here and there a +dark, lack-lustre eye turned upon the newcomer. Out of the black +shadows there glimmered little red circles of light, now bright, +now faint, as the burning poison waxed or waned in the bowls of +the metal pipes. The most lay silent, but some muttered to +themselves, and others talked together in a strange, low, +monotonous voice, their conversation coming in gushes, and then +suddenly tailing off into silence, each mumbling out his own +thoughts and paying little heed to the words of his neighbour. At +the farther end was a small brazier of burning charcoal, beside +which on a three-legged wooden stool there sat a tall, thin old +man, with his jaw resting upon his two fists, and his elbows upon +his knees, staring into the fire. + +As I entered, a sallow Malay attendant had hurried up with a pipe +for me and a supply of the drug, beckoning me to an empty berth. + +"Thank you. I have not come to stay," said I. "There is a friend +of mine here, Mr. Isa Whitney, and I wish to speak with him." + +There was a movement and an exclamation from my right, and +peering through the gloom, I saw Whitney, pale, haggard, and +unkempt, staring out at me. + +"My God! It's Watson," said he. He was in a pitiable state of +reaction, with every nerve in a twitter. "I say, Watson, what +o'clock is it?" + +"Nearly eleven." + +"Of what day?" + +"Of Friday, June 19th." + +"Good heavens! I thought it was Wednesday. It is Wednesday. What +d'you want to frighten a chap for?" He sank his face onto his +arms and began to sob in a high treble key. + +"I tell you that it is Friday, man. Your wife has been waiting +this two days for you. You should be ashamed of yourself!" + +"So I am. But you've got mixed, Watson, for I have only been here +a few hours, three pipes, four pipes--I forget how many. But I'll +go home with you. I wouldn't frighten Kate--poor little Kate. +Give me your hand! Have you a cab?" + +"Yes, I have one waiting." + +"Then I shall go in it. But I must owe something. Find what I +owe, Watson. I am all off colour. I can do nothing for myself." + +I walked down the narrow passage between the double row of +sleepers, holding my breath to keep out the vile, stupefying +fumes of the drug, and looking about for the manager. As I passed +the tall man who sat by the brazier I felt a sudden pluck at my +skirt, and a low voice whispered, "Walk past me, and then look +back at me." The words fell quite distinctly upon my ear. I +glanced down. They could only have come from the old man at my +side, and yet he sat now as absorbed as ever, very thin, very +wrinkled, bent with age, an opium pipe dangling down from between +his knees, as though it had dropped in sheer lassitude from his +fingers. I took two steps forward and looked back. It took all my +self-control to prevent me from breaking out into a cry of +astonishment. He had turned his back so that none could see him +but I. His form had filled out, his wrinkles were gone, the dull +eyes had regained their fire, and there, sitting by the fire and +grinning at my surprise, was none other than Sherlock Holmes. He +made a slight motion to me to approach him, and instantly, as he +turned his face half round to the company once more, subsided +into a doddering, loose-lipped senility. + +"Holmes!" I whispered, "what on earth are you doing in this den?" + +"As low as you can," he answered; "I have excellent ears. If you +would have the great kindness to get rid of that sottish friend +of yours I should be exceedingly glad to have a little talk with +you." + +"I have a cab outside." + +"Then pray send him home in it. You may safely trust him, for he +appears to be too limp to get into any mischief. I should +recommend you also to send a note by the cabman to your wife to +say that you have thrown in your lot with me. If you will wait +outside, I shall be with you in five minutes." + +It was difficult to refuse any of Sherlock Holmes' requests, for +they were always so exceedingly definite, and put forward with +such a quiet air of mastery. I felt, however, that when Whitney +was once confined in the cab my mission was practically +accomplished; and for the rest, I could not wish anything better +than to be associated with my friend in one of those singular +adventures which were the normal condition of his existence. In a +few minutes I had written my note, paid Whitney's bill, led him +out to the cab, and seen him driven through the darkness. In a +very short time a decrepit figure had emerged from the opium den, +and I was walking down the street with Sherlock Holmes. For two +streets he shuffled along with a bent back and an uncertain foot. +Then, glancing quickly round, he straightened himself out and +burst into a hearty fit of laughter. + +"I suppose, Watson," said he, "that you imagine that I have added +opium-smoking to cocaine injections, and all the other little +weaknesses on which you have favoured me with your medical +views." + +"I was certainly surprised to find you there." + +"But not more so than I to find you." + +"I came to find a friend." + +"And I to find an enemy." + +"An enemy?" + +"Yes; one of my natural enemies, or, shall I say, my natural +prey. Briefly, Watson, I am in the midst of a very remarkable +inquiry, and I have hoped to find a clue in the incoherent +ramblings of these sots, as I have done before now. Had I been +recognised in that den my life would not have been worth an +hour's purchase; for I have used it before now for my own +purposes, and the rascally Lascar who runs it has sworn to have +vengeance upon me. There is a trap-door at the back of that +building, near the corner of Paul's Wharf, which could tell some +strange tales of what has passed through it upon the moonless +nights." + +"What! You do not mean bodies?" + +"Ay, bodies, Watson. We should be rich men if we had 1000 pounds +for every poor devil who has been done to death in that den. It +is the vilest murder-trap on the whole riverside, and I fear that +Neville St. Clair has entered it never to leave it more. But our +trap should be here." He put his two forefingers between his +teeth and whistled shrilly--a signal which was answered by a +similar whistle from the distance, followed shortly by the rattle +of wheels and the clink of horses' hoofs. + +"Now, Watson," said Holmes, as a tall dog-cart dashed up through +the gloom, throwing out two golden tunnels of yellow light from +its side lanterns. "You'll come with me, won't you?" + +"If I can be of use." + +"Oh, a trusty comrade is always of use; and a chronicler still +more so. My room at The Cedars is a double-bedded one." + +"The Cedars?" + +"Yes; that is Mr. St. Clair's house. I am staying there while I +conduct the inquiry." + +"Where is it, then?" + +"Near Lee, in Kent. We have a seven-mile drive before us." + +"But I am all in the dark." + +"Of course you are. You'll know all about it presently. Jump up +here. All right, John; we shall not need you. Here's half a +crown. Look out for me to-morrow, about eleven. Give her her +head. So long, then!" + +He flicked the horse with his whip, and we dashed away through +the endless succession of sombre and deserted streets, which +widened gradually, until we were flying across a broad +balustraded bridge, with the murky river flowing sluggishly +beneath us. Beyond lay another dull wilderness of bricks and +mortar, its silence broken only by the heavy, regular footfall of +the policeman, or the songs and shouts of some belated party of +revellers. A dull wrack was drifting slowly across the sky, and a +star or two twinkled dimly here and there through the rifts of +the clouds. Holmes drove in silence, with his head sunk upon his +breast, and the air of a man who is lost in thought, while I sat +beside him, curious to learn what this new quest might be which +seemed to tax his powers so sorely, and yet afraid to break in +upon the current of his thoughts. We had driven several miles, +and were beginning to get to the fringe of the belt of suburban +villas, when he shook himself, shrugged his shoulders, and lit up +his pipe with the air of a man who has satisfied himself that he +is acting for the best. + +"You have a grand gift of silence, Watson," said he. "It makes +you quite invaluable as a companion. 'Pon my word, it is a great +thing for me to have someone to talk to, for my own thoughts are +not over-pleasant. I was wondering what I should say to this dear +little woman to-night when she meets me at the door." + +"You forget that I know nothing about it." + +"I shall just have time to tell you the facts of the case before +we get to Lee. It seems absurdly simple, and yet, somehow I can +get nothing to go upon. There's plenty of thread, no doubt, but I +can't get the end of it into my hand. Now, I'll state the case +clearly and concisely to you, Watson, and maybe you can see a +spark where all is dark to me." + +"Proceed, then." + +"Some years ago--to be definite, in May, 1884--there came to Lee +a gentleman, Neville St. Clair by name, who appeared to have +plenty of money. He took a large villa, laid out the grounds very +nicely, and lived generally in good style. By degrees he made +friends in the neighbourhood, and in 1887 he married the daughter +of a local brewer, by whom he now has two children. He had no +occupation, but was interested in several companies and went into +town as a rule in the morning, returning by the 5:14 from Cannon +Street every night. Mr. St. Clair is now thirty-seven years of +age, is a man of temperate habits, a good husband, a very +affectionate father, and a man who is popular with all who know +him. I may add that his whole debts at the present moment, as far +as we have been able to ascertain, amount to 88 pounds 10s., while +he has 220 pounds standing to his credit in the Capital and +Counties Bank. There is no reason, therefore, to think that money +troubles have been weighing upon his mind. + +"Last Monday Mr. Neville St. Clair went into town rather earlier +than usual, remarking before he started that he had two important +commissions to perform, and that he would bring his little boy +home a box of bricks. Now, by the merest chance, his wife +received a telegram upon this same Monday, very shortly after his +departure, to the effect that a small parcel of considerable +value which she had been expecting was waiting for her at the +offices of the Aberdeen Shipping Company. Now, if you are well up +in your London, you will know that the office of the company is +in Fresno Street, which branches out of Upper Swandam Lane, where +you found me to-night. Mrs. St. Clair had her lunch, started for +the City, did some shopping, proceeded to the company's office, +got her packet, and found herself at exactly 4:35 walking through +Swandam Lane on her way back to the station. Have you followed me +so far?" + +"It is very clear." + +"If you remember, Monday was an exceedingly hot day, and Mrs. St. +Clair walked slowly, glancing about in the hope of seeing a cab, +as she did not like the neighbourhood in which she found herself. +While she was walking in this way down Swandam Lane, she suddenly +heard an ejaculation or cry, and was struck cold to see her +husband looking down at her and, as it seemed to her, beckoning +to her from a second-floor window. The window was open, and she +distinctly saw his face, which she describes as being terribly +agitated. He waved his hands frantically to her, and then +vanished from the window so suddenly that it seemed to her that +he had been plucked back by some irresistible force from behind. +One singular point which struck her quick feminine eye was that +although he wore some dark coat, such as he had started to town +in, he had on neither collar nor necktie. + +"Convinced that something was amiss with him, she rushed down the +steps--for the house was none other than the opium den in which +you found me to-night--and running through the front room she +attempted to ascend the stairs which led to the first floor. At +the foot of the stairs, however, she met this Lascar scoundrel of +whom I have spoken, who thrust her back and, aided by a Dane, who +acts as assistant there, pushed her out into the street. Filled +with the most maddening doubts and fears, she rushed down the +lane and, by rare good-fortune, met in Fresno Street a number of +constables with an inspector, all on their way to their beat. The +inspector and two men accompanied her back, and in spite of the +continued resistance of the proprietor, they made their way to +the room in which Mr. St. Clair had last been seen. There was no +sign of him there. In fact, in the whole of that floor there was +no one to be found save a crippled wretch of hideous aspect, who, +it seems, made his home there. Both he and the Lascar stoutly +swore that no one else had been in the front room during the +afternoon. So determined was their denial that the inspector was +staggered, and had almost come to believe that Mrs. St. Clair had +been deluded when, with a cry, she sprang at a small deal box +which lay upon the table and tore the lid from it. Out there fell +a cascade of children's bricks. It was the toy which he had +promised to bring home. + +"This discovery, and the evident confusion which the cripple +showed, made the inspector realise that the matter was serious. +The rooms were carefully examined, and results all pointed to an +abominable crime. The front room was plainly furnished as a +sitting-room and led into a small bedroom, which looked out upon +the back of one of the wharves. Between the wharf and the bedroom +window is a narrow strip, which is dry at low tide but is covered +at high tide with at least four and a half feet of water. The +bedroom window was a broad one and opened from below. On +examination traces of blood were to be seen upon the windowsill, +and several scattered drops were visible upon the wooden floor of +the bedroom. Thrust away behind a curtain in the front room were +all the clothes of Mr. Neville St. Clair, with the exception of +his coat. His boots, his socks, his hat, and his watch--all were +there. There were no signs of violence upon any of these +garments, and there were no other traces of Mr. Neville St. +Clair. Out of the window he must apparently have gone for no +other exit could be discovered, and the ominous bloodstains upon +the sill gave little promise that he could save himself by +swimming, for the tide was at its very highest at the moment of +the tragedy. + +"And now as to the villains who seemed to be immediately +implicated in the matter. The Lascar was known to be a man of the +vilest antecedents, but as, by Mrs. St. Clair's story, he was +known to have been at the foot of the stair within a very few +seconds of her husband's appearance at the window, he could +hardly have been more than an accessory to the crime. His defence +was one of absolute ignorance, and he protested that he had no +knowledge as to the doings of Hugh Boone, his lodger, and that he +could not account in any way for the presence of the missing +gentleman's clothes. + +"So much for the Lascar manager. Now for the sinister cripple who +lives upon the second floor of the opium den, and who was +certainly the last human being whose eyes rested upon Neville St. +Clair. His name is Hugh Boone, and his hideous face is one which +is familiar to every man who goes much to the City. He is a +professional beggar, though in order to avoid the police +regulations he pretends to a small trade in wax vestas. Some +little distance down Threadneedle Street, upon the left-hand +side, there is, as you may have remarked, a small angle in the +wall. Here it is that this creature takes his daily seat, +cross-legged with his tiny stock of matches on his lap, and as he +is a piteous spectacle a small rain of charity descends into the +greasy leather cap which lies upon the pavement beside him. I +have watched the fellow more than once before ever I thought of +making his professional acquaintance, and I have been surprised +at the harvest which he has reaped in a short time. His +appearance, you see, is so remarkable that no one can pass him +without observing him. A shock of orange hair, a pale face +disfigured by a horrible scar, which, by its contraction, has +turned up the outer edge of his upper lip, a bulldog chin, and a +pair of very penetrating dark eyes, which present a singular +contrast to the colour of his hair, all mark him out from amid +the common crowd of mendicants and so, too, does his wit, for he +is ever ready with a reply to any piece of chaff which may be +thrown at him by the passers-by. This is the man whom we now +learn to have been the lodger at the opium den, and to have been +the last man to see the gentleman of whom we are in quest." + +"But a cripple!" said I. "What could he have done single-handed +against a man in the prime of life?" + +"He is a cripple in the sense that he walks with a limp; but in +other respects he appears to be a powerful and well-nurtured man. +Surely your medical experience would tell you, Watson, that +weakness in one limb is often compensated for by exceptional +strength in the others." + +"Pray continue your narrative." + +"Mrs. St. Clair had fainted at the sight of the blood upon the +window, and she was escorted home in a cab by the police, as her +presence could be of no help to them in their investigations. +Inspector Barton, who had charge of the case, made a very careful +examination of the premises, but without finding anything which +threw any light upon the matter. One mistake had been made in not +arresting Boone instantly, as he was allowed some few minutes +during which he might have communicated with his friend the +Lascar, but this fault was soon remedied, and he was seized and +searched, without anything being found which could incriminate +him. There were, it is true, some blood-stains upon his right +shirt-sleeve, but he pointed to his ring-finger, which had been +cut near the nail, and explained that the bleeding came from +there, adding that he had been to the window not long before, and +that the stains which had been observed there came doubtless from +the same source. He denied strenuously having ever seen Mr. +Neville St. Clair and swore that the presence of the clothes in +his room was as much a mystery to him as to the police. As to +Mrs. St. Clair's assertion that she had actually seen her husband +at the window, he declared that she must have been either mad or +dreaming. He was removed, loudly protesting, to the +police-station, while the inspector remained upon the premises in +the hope that the ebbing tide might afford some fresh clue. + +"And it did, though they hardly found upon the mud-bank what they +had feared to find. It was Neville St. Clair's coat, and not +Neville St. Clair, which lay uncovered as the tide receded. And +what do you think they found in the pockets?" + +"I cannot imagine." + +"No, I don't think you would guess. Every pocket stuffed with +pennies and half-pennies--421 pennies and 270 half-pennies. It +was no wonder that it had not been swept away by the tide. But a +human body is a different matter. There is a fierce eddy between +the wharf and the house. It seemed likely enough that the +weighted coat had remained when the stripped body had been sucked +away into the river." + +"But I understand that all the other clothes were found in the +room. Would the body be dressed in a coat alone?" + +"No, sir, but the facts might be met speciously enough. Suppose +that this man Boone had thrust Neville St. Clair through the +window, there is no human eye which could have seen the deed. +What would he do then? It would of course instantly strike him +that he must get rid of the tell-tale garments. He would seize +the coat, then, and be in the act of throwing it out, when it +would occur to him that it would swim and not sink. He has little +time, for he has heard the scuffle downstairs when the wife tried +to force her way up, and perhaps he has already heard from his +Lascar confederate that the police are hurrying up the street. +There is not an instant to be lost. He rushes to some secret +hoard, where he has accumulated the fruits of his beggary, and he +stuffs all the coins upon which he can lay his hands into the +pockets to make sure of the coat's sinking. He throws it out, and +would have done the same with the other garments had not he heard +the rush of steps below, and only just had time to close the +window when the police appeared." + +"It certainly sounds feasible." + +"Well, we will take it as a working hypothesis for want of a +better. Boone, as I have told you, was arrested and taken to the +station, but it could not be shown that there had ever before +been anything against him. He had for years been known as a +professional beggar, but his life appeared to have been a very +quiet and innocent one. There the matter stands at present, and +the questions which have to be solved--what Neville St. Clair was +doing in the opium den, what happened to him when there, where is +he now, and what Hugh Boone had to do with his disappearance--are +all as far from a solution as ever. I confess that I cannot +recall any case within my experience which looked at the first +glance so simple and yet which presented such difficulties." + +While Sherlock Holmes had been detailing this singular series of +events, we had been whirling through the outskirts of the great +town until the last straggling houses had been left behind, and +we rattled along with a country hedge upon either side of us. +Just as he finished, however, we drove through two scattered +villages, where a few lights still glimmered in the windows. + +"We are on the outskirts of Lee," said my companion. "We have +touched on three English counties in our short drive, starting in +Middlesex, passing over an angle of Surrey, and ending in Kent. +See that light among the trees? That is The Cedars, and beside +that lamp sits a woman whose anxious ears have already, I have +little doubt, caught the clink of our horse's feet." + +"But why are you not conducting the case from Baker Street?" I +asked. + +"Because there are many inquiries which must be made out here. +Mrs. St. Clair has most kindly put two rooms at my disposal, and +you may rest assured that she will have nothing but a welcome for +my friend and colleague. I hate to meet her, Watson, when I have +no news of her husband. Here we are. Whoa, there, whoa!" + +We had pulled up in front of a large villa which stood within its +own grounds. A stable-boy had run out to the horse's head, and +springing down, I followed Holmes up the small, winding +gravel-drive which led to the house. As we approached, the door +flew open, and a little blonde woman stood in the opening, clad +in some sort of light mousseline de soie, with a touch of fluffy +pink chiffon at her neck and wrists. She stood with her figure +outlined against the flood of light, one hand upon the door, one +half-raised in her eagerness, her body slightly bent, her head +and face protruded, with eager eyes and parted lips, a standing +question. + +"Well?" she cried, "well?" And then, seeing that there were two +of us, she gave a cry of hope which sank into a groan as she saw +that my companion shook his head and shrugged his shoulders. + +"No good news?" + +"None." + +"No bad?" + +"No." + +"Thank God for that. But come in. You must be weary, for you have +had a long day." + +"This is my friend, Dr. Watson. He has been of most vital use to +me in several of my cases, and a lucky chance has made it +possible for me to bring him out and associate him with this +investigation." + +"I am delighted to see you," said she, pressing my hand warmly. +"You will, I am sure, forgive anything that may be wanting in our +arrangements, when you consider the blow which has come so +suddenly upon us." + +"My dear madam," said I, "I am an old campaigner, and if I were +not I can very well see that no apology is needed. If I can be of +any assistance, either to you or to my friend here, I shall be +indeed happy." + +"Now, Mr. Sherlock Holmes," said the lady as we entered a +well-lit dining-room, upon the table of which a cold supper had +been laid out, "I should very much like to ask you one or two +plain questions, to which I beg that you will give a plain +answer." + +"Certainly, madam." + +"Do not trouble about my feelings. I am not hysterical, nor given +to fainting. I simply wish to hear your real, real opinion." + +"Upon what point?" + +"In your heart of hearts, do you think that Neville is alive?" + +Sherlock Holmes seemed to be embarrassed by the question. +"Frankly, now!" she repeated, standing upon the rug and looking +keenly down at him as he leaned back in a basket-chair. + +"Frankly, then, madam, I do not." + +"You think that he is dead?" + +"I do." + +"Murdered?" + +"I don't say that. Perhaps." + +"And on what day did he meet his death?" + +"On Monday." + +"Then perhaps, Mr. Holmes, you will be good enough to explain how +it is that I have received a letter from him to-day." + +Sherlock Holmes sprang out of his chair as if he had been +galvanised. + +"What!" he roared. + +"Yes, to-day." She stood smiling, holding up a little slip of +paper in the air. + +"May I see it?" + +"Certainly." + +He snatched it from her in his eagerness, and smoothing it out +upon the table he drew over the lamp and examined it intently. I +had left my chair and was gazing at it over his shoulder. The +envelope was a very coarse one and was stamped with the Gravesend +postmark and with the date of that very day, or rather of the day +before, for it was considerably after midnight. + +"Coarse writing," murmured Holmes. "Surely this is not your +husband's writing, madam." + +"No, but the enclosure is." + +"I perceive also that whoever addressed the envelope had to go +and inquire as to the address." + +"How can you tell that?" + +"The name, you see, is in perfectly black ink, which has dried +itself. The rest is of the greyish colour, which shows that +blotting-paper has been used. If it had been written straight +off, and then blotted, none would be of a deep black shade. This +man has written the name, and there has then been a pause before +he wrote the address, which can only mean that he was not +familiar with it. It is, of course, a trifle, but there is +nothing so important as trifles. Let us now see the letter. Ha! +there has been an enclosure here!" + +"Yes, there was a ring. His signet-ring." + +"And you are sure that this is your husband's hand?" + +"One of his hands." + +"One?" + +"His hand when he wrote hurriedly. It is very unlike his usual +writing, and yet I know it well." + +"'Dearest do not be frightened. All will come well. There is a +huge error which it may take some little time to rectify. +Wait in patience.--NEVILLE.' Written in pencil upon the fly-leaf +of a book, octavo size, no water-mark. Hum! Posted to-day in +Gravesend by a man with a dirty thumb. Ha! And the flap has been +gummed, if I am not very much in error, by a person who had been +chewing tobacco. And you have no doubt that it is your husband's +hand, madam?" + +"None. Neville wrote those words." + +"And they were posted to-day at Gravesend. Well, Mrs. St. Clair, +the clouds lighten, though I should not venture to say that the +danger is over." + +"But he must be alive, Mr. Holmes." + +"Unless this is a clever forgery to put us on the wrong scent. +The ring, after all, proves nothing. It may have been taken from +him." + +"No, no; it is, it is his very own writing!" + +"Very well. It may, however, have been written on Monday and only +posted to-day." + +"That is possible." + +"If so, much may have happened between." + +"Oh, you must not discourage me, Mr. Holmes. I know that all is +well with him. There is so keen a sympathy between us that I +should know if evil came upon him. On the very day that I saw him +last he cut himself in the bedroom, and yet I in the dining-room +rushed upstairs instantly with the utmost certainty that +something had happened. Do you think that I would respond to such +a trifle and yet be ignorant of his death?" + +"I have seen too much not to know that the impression of a woman +may be more valuable than the conclusion of an analytical +reasoner. And in this letter you certainly have a very strong +piece of evidence to corroborate your view. But if your husband +is alive and able to write letters, why should he remain away +from you?" + +"I cannot imagine. It is unthinkable." + +"And on Monday he made no remarks before leaving you?" + +"No." + +"And you were surprised to see him in Swandam Lane?" + +"Very much so." + +"Was the window open?" + +"Yes." + +"Then he might have called to you?" + +"He might." + +"He only, as I understand, gave an inarticulate cry?" + +"Yes." + +"A call for help, you thought?" + +"Yes. He waved his hands." + +"But it might have been a cry of surprise. Astonishment at the +unexpected sight of you might cause him to throw up his hands?" + +"It is possible." + +"And you thought he was pulled back?" + +"He disappeared so suddenly." + +"He might have leaped back. You did not see anyone else in the +room?" + +"No, but this horrible man confessed to having been there, and +the Lascar was at the foot of the stairs." + +"Quite so. Your husband, as far as you could see, had his +ordinary clothes on?" + +"But without his collar or tie. I distinctly saw his bare +throat." + +"Had he ever spoken of Swandam Lane?" + +"Never." + +"Had he ever showed any signs of having taken opium?" + +"Never." + +"Thank you, Mrs. St. Clair. Those are the principal points about +which I wished to be absolutely clear. We shall now have a little +supper and then retire, for we may have a very busy day +to-morrow." + +A large and comfortable double-bedded room had been placed at our +disposal, and I was quickly between the sheets, for I was weary +after my night of adventure. Sherlock Holmes was a man, however, +who, when he had an unsolved problem upon his mind, would go for +days, and even for a week, without rest, turning it over, +rearranging his facts, looking at it from every point of view +until he had either fathomed it or convinced himself that his +data were insufficient. It was soon evident to me that he was now +preparing for an all-night sitting. He took off his coat and +waistcoat, put on a large blue dressing-gown, and then wandered +about the room collecting pillows from his bed and cushions from +the sofa and armchairs. With these he constructed a sort of +Eastern divan, upon which he perched himself cross-legged, with +an ounce of shag tobacco and a box of matches laid out in front +of him. In the dim light of the lamp I saw him sitting there, an +old briar pipe between his lips, his eyes fixed vacantly upon the +corner of the ceiling, the blue smoke curling up from him, +silent, motionless, with the light shining upon his strong-set +aquiline features. So he sat as I dropped off to sleep, and so he +sat when a sudden ejaculation caused me to wake up, and I found +the summer sun shining into the apartment. The pipe was still +between his lips, the smoke still curled upward, and the room was +full of a dense tobacco haze, but nothing remained of the heap of +shag which I had seen upon the previous night. + +"Awake, Watson?" he asked. + +"Yes." + +"Game for a morning drive?" + +"Certainly." + +"Then dress. No one is stirring yet, but I know where the +stable-boy sleeps, and we shall soon have the trap out." He +chuckled to himself as he spoke, his eyes twinkled, and he seemed +a different man to the sombre thinker of the previous night. + +As I dressed I glanced at my watch. It was no wonder that no one +was stirring. It was twenty-five minutes past four. I had hardly +finished when Holmes returned with the news that the boy was +putting in the horse. + +"I want to test a little theory of mine," said he, pulling on his +boots. "I think, Watson, that you are now standing in the +presence of one of the most absolute fools in Europe. I deserve +to be kicked from here to Charing Cross. But I think I have the +key of the affair now." + +"And where is it?" I asked, smiling. + +"In the bathroom," he answered. "Oh, yes, I am not joking," he +continued, seeing my look of incredulity. "I have just been +there, and I have taken it out, and I have got it in this +Gladstone bag. Come on, my boy, and we shall see whether it will +not fit the lock." + +We made our way downstairs as quietly as possible, and out into +the bright morning sunshine. In the road stood our horse and +trap, with the half-clad stable-boy waiting at the head. We both +sprang in, and away we dashed down the London Road. A few country +carts were stirring, bearing in vegetables to the metropolis, but +the lines of villas on either side were as silent and lifeless as +some city in a dream. + +"It has been in some points a singular case," said Holmes, +flicking the horse on into a gallop. "I confess that I have been +as blind as a mole, but it is better to learn wisdom late than +never to learn it at all." + +In town the earliest risers were just beginning to look sleepily +from their windows as we drove through the streets of the Surrey +side. Passing down the Waterloo Bridge Road we crossed over the +river, and dashing up Wellington Street wheeled sharply to the +right and found ourselves in Bow Street. Sherlock Holmes was well +known to the force, and the two constables at the door saluted +him. One of them held the horse's head while the other led us in. + +"Who is on duty?" asked Holmes. + +"Inspector Bradstreet, sir." + +"Ah, Bradstreet, how are you?" A tall, stout official had come +down the stone-flagged passage, in a peaked cap and frogged +jacket. "I wish to have a quiet word with you, Bradstreet." +"Certainly, Mr. Holmes. Step into my room here." It was a small, +office-like room, with a huge ledger upon the table, and a +telephone projecting from the wall. The inspector sat down at his +desk. + +"What can I do for you, Mr. Holmes?" + +"I called about that beggarman, Boone--the one who was charged +with being concerned in the disappearance of Mr. Neville St. +Clair, of Lee." + +"Yes. He was brought up and remanded for further inquiries." + +"So I heard. You have him here?" + +"In the cells." + +"Is he quiet?" + +"Oh, he gives no trouble. But he is a dirty scoundrel." + +"Dirty?" + +"Yes, it is all we can do to make him wash his hands, and his +face is as black as a tinker's. Well, when once his case has been +settled, he will have a regular prison bath; and I think, if you +saw him, you would agree with me that he needed it." + +"I should like to see him very much." + +"Would you? That is easily done. Come this way. You can leave +your bag." + +"No, I think that I'll take it." + +"Very good. Come this way, if you please." He led us down a +passage, opened a barred door, passed down a winding stair, and +brought us to a whitewashed corridor with a line of doors on each +side. + +"The third on the right is his," said the inspector. "Here it +is!" He quietly shot back a panel in the upper part of the door +and glanced through. + +"He is asleep," said he. "You can see him very well." + +We both put our eyes to the grating. The prisoner lay with his +face towards us, in a very deep sleep, breathing slowly and +heavily. He was a middle-sized man, coarsely clad as became his +calling, with a coloured shirt protruding through the rent in his +tattered coat. He was, as the inspector had said, extremely +dirty, but the grime which covered his face could not conceal its +repulsive ugliness. A broad wheal from an old scar ran right +across it from eye to chin, and by its contraction had turned up +one side of the upper lip, so that three teeth were exposed in a +perpetual snarl. A shock of very bright red hair grew low over +his eyes and forehead. + +"He's a beauty, isn't he?" said the inspector. + +"He certainly needs a wash," remarked Holmes. "I had an idea that +he might, and I took the liberty of bringing the tools with me." +He opened the Gladstone bag as he spoke, and took out, to my +astonishment, a very large bath-sponge. + +"He! he! You are a funny one," chuckled the inspector. + +"Now, if you will have the great goodness to open that door very +quietly, we will soon make him cut a much more respectable +figure." + +"Well, I don't know why not," said the inspector. "He doesn't +look a credit to the Bow Street cells, does he?" He slipped his +key into the lock, and we all very quietly entered the cell. The +sleeper half turned, and then settled down once more into a deep +slumber. Holmes stooped to the water-jug, moistened his sponge, +and then rubbed it twice vigorously across and down the +prisoner's face. + +"Let me introduce you," he shouted, "to Mr. Neville St. Clair, of +Lee, in the county of Kent." + +Never in my life have I seen such a sight. The man's face peeled +off under the sponge like the bark from a tree. Gone was the +coarse brown tint! Gone, too, was the horrid scar which had +seamed it across, and the twisted lip which had given the +repulsive sneer to the face! A twitch brought away the tangled +red hair, and there, sitting up in his bed, was a pale, +sad-faced, refined-looking man, black-haired and smooth-skinned, +rubbing his eyes and staring about him with sleepy bewilderment. +Then suddenly realising the exposure, he broke into a scream and +threw himself down with his face to the pillow. + +"Great heavens!" cried the inspector, "it is, indeed, the missing +man. I know him from the photograph." + +The prisoner turned with the reckless air of a man who abandons +himself to his destiny. "Be it so," said he. "And pray what am I +charged with?" + +"With making away with Mr. Neville St.-- Oh, come, you can't be +charged with that unless they make a case of attempted suicide of +it," said the inspector with a grin. "Well, I have been +twenty-seven years in the force, but this really takes the cake." + +"If I am Mr. Neville St. Clair, then it is obvious that no crime +has been committed, and that, therefore, I am illegally +detained." + +"No crime, but a very great error has been committed," said +Holmes. "You would have done better to have trusted your wife." + +"It was not the wife; it was the children," groaned the prisoner. +"God help me, I would not have them ashamed of their father. My +God! What an exposure! What can I do?" + +Sherlock Holmes sat down beside him on the couch and patted him +kindly on the shoulder. + +"If you leave it to a court of law to clear the matter up," said +he, "of course you can hardly avoid publicity. On the other hand, +if you convince the police authorities that there is no possible +case against you, I do not know that there is any reason that the +details should find their way into the papers. Inspector +Bradstreet would, I am sure, make notes upon anything which you +might tell us and submit it to the proper authorities. The case +would then never go into court at all." + +"God bless you!" cried the prisoner passionately. "I would have +endured imprisonment, ay, even execution, rather than have left +my miserable secret as a family blot to my children. + +"You are the first who have ever heard my story. My father was a +schoolmaster in Chesterfield, where I received an excellent +education. I travelled in my youth, took to the stage, and +finally became a reporter on an evening paper in London. One day +my editor wished to have a series of articles upon begging in the +metropolis, and I volunteered to supply them. There was the point +from which all my adventures started. It was only by trying +begging as an amateur that I could get the facts upon which to +base my articles. When an actor I had, of course, learned all the +secrets of making up, and had been famous in the green-room for +my skill. I took advantage now of my attainments. I painted my +face, and to make myself as pitiable as possible I made a good +scar and fixed one side of my lip in a twist by the aid of a +small slip of flesh-coloured plaster. Then with a red head of +hair, and an appropriate dress, I took my station in the business +part of the city, ostensibly as a match-seller but really as a +beggar. For seven hours I plied my trade, and when I returned +home in the evening I found to my surprise that I had received no +less than 26s. 4d. + +"I wrote my articles and thought little more of the matter until, +some time later, I backed a bill for a friend and had a writ +served upon me for 25 pounds. I was at my wit's end where to get +the money, but a sudden idea came to me. I begged a fortnight's +grace from the creditor, asked for a holiday from my employers, +and spent the time in begging in the City under my disguise. In +ten days I had the money and had paid the debt. + +"Well, you can imagine how hard it was to settle down to arduous +work at 2 pounds a week when I knew that I could earn as much in +a day by smearing my face with a little paint, laying my cap on +the ground, and sitting still. It was a long fight between my +pride and the money, but the dollars won at last, and I threw up +reporting and sat day after day in the corner which I had first +chosen, inspiring pity by my ghastly face and filling my pockets +with coppers. Only one man knew my secret. He was the keeper of a +low den in which I used to lodge in Swandam Lane, where I could +every morning emerge as a squalid beggar and in the evenings +transform myself into a well-dressed man about town. This fellow, +a Lascar, was well paid by me for his rooms, so that I knew that +my secret was safe in his possession. + +"Well, very soon I found that I was saving considerable sums of +money. I do not mean that any beggar in the streets of London +could earn 700 pounds a year--which is less than my average +takings--but I had exceptional advantages in my power of making +up, and also in a facility of repartee, which improved by +practice and made me quite a recognised character in the City. +All day a stream of pennies, varied by silver, poured in upon me, +and it was a very bad day in which I failed to take 2 pounds. + +"As I grew richer I grew more ambitious, took a house in the +country, and eventually married, without anyone having a +suspicion as to my real occupation. My dear wife knew that I had +business in the City. She little knew what. + +"Last Monday I had finished for the day and was dressing in my +room above the opium den when I looked out of my window and saw, +to my horror and astonishment, that my wife was standing in the +street, with her eyes fixed full upon me. I gave a cry of +surprise, threw up my arms to cover my face, and, rushing to my +confidant, the Lascar, entreated him to prevent anyone from +coming up to me. I heard her voice downstairs, but I knew that +she could not ascend. Swiftly I threw off my clothes, pulled on +those of a beggar, and put on my pigments and wig. Even a wife's +eyes could not pierce so complete a disguise. But then it +occurred to me that there might be a search in the room, and that +the clothes might betray me. I threw open the window, reopening +by my violence a small cut which I had inflicted upon myself in +the bedroom that morning. Then I seized my coat, which was +weighted by the coppers which I had just transferred to it from +the leather bag in which I carried my takings. I hurled it out of +the window, and it disappeared into the Thames. The other clothes +would have followed, but at that moment there was a rush of +constables up the stair, and a few minutes after I found, rather, +I confess, to my relief, that instead of being identified as Mr. +Neville St. Clair, I was arrested as his murderer. + +"I do not know that there is anything else for me to explain. I +was determined to preserve my disguise as long as possible, and +hence my preference for a dirty face. Knowing that my wife would +be terribly anxious, I slipped off my ring and confided it to the +Lascar at a moment when no constable was watching me, together +with a hurried scrawl, telling her that she had no cause to +fear." + +"That note only reached her yesterday," said Holmes. + +"Good God! What a week she must have spent!" + +"The police have watched this Lascar," said Inspector Bradstreet, +"and I can quite understand that he might find it difficult to +post a letter unobserved. Probably he handed it to some sailor +customer of his, who forgot all about it for some days." + +"That was it," said Holmes, nodding approvingly; "I have no doubt +of it. But have you never been prosecuted for begging?" + +"Many times; but what was a fine to me?" + +"It must stop here, however," said Bradstreet. "If the police are +to hush this thing up, there must be no more of Hugh Boone." + +"I have sworn it by the most solemn oaths which a man can take." + +"In that case I think that it is probable that no further steps +may be taken. But if you are found again, then all must come out. +I am sure, Mr. Holmes, that we are very much indebted to you for +having cleared the matter up. I wish I knew how you reach your +results." + +"I reached this one," said my friend, "by sitting upon five +pillows and consuming an ounce of shag. I think, Watson, that if +we drive to Baker Street we shall just be in time for breakfast." + + + +VII. THE ADVENTURE OF THE BLUE CARBUNCLE + +I had called upon my friend Sherlock Holmes upon the second +morning after Christmas, with the intention of wishing him the +compliments of the season. He was lounging upon the sofa in a +purple dressing-gown, a pipe-rack within his reach upon the +right, and a pile of crumpled morning papers, evidently newly +studied, near at hand. Beside the couch was a wooden chair, and +on the angle of the back hung a very seedy and disreputable +hard-felt hat, much the worse for wear, and cracked in several +places. A lens and a forceps lying upon the seat of the chair +suggested that the hat had been suspended in this manner for the +purpose of examination. + +"You are engaged," said I; "perhaps I interrupt you." + +"Not at all. I am glad to have a friend with whom I can discuss +my results. The matter is a perfectly trivial one"--he jerked his +thumb in the direction of the old hat--"but there are points in +connection with it which are not entirely devoid of interest and +even of instruction." + +I seated myself in his armchair and warmed my hands before his +crackling fire, for a sharp frost had set in, and the windows +were thick with the ice crystals. "I suppose," I remarked, "that, +homely as it looks, this thing has some deadly story linked on to +it--that it is the clue which will guide you in the solution of +some mystery and the punishment of some crime." + +"No, no. No crime," said Sherlock Holmes, laughing. "Only one of +those whimsical little incidents which will happen when you have +four million human beings all jostling each other within the +space of a few square miles. Amid the action and reaction of so +dense a swarm of humanity, every possible combination of events +may be expected to take place, and many a little problem will be +presented which may be striking and bizarre without being +criminal. We have already had experience of such." + +"So much so," I remarked, "that of the last six cases which I +have added to my notes, three have been entirely free of any +legal crime." + +"Precisely. You allude to my attempt to recover the Irene Adler +papers, to the singular case of Miss Mary Sutherland, and to the +adventure of the man with the twisted lip. Well, I have no doubt +that this small matter will fall into the same innocent category. +You know Peterson, the commissionaire?" + +"Yes." + +"It is to him that this trophy belongs." + +"It is his hat." + +"No, no, he found it. Its owner is unknown. I beg that you will +look upon it not as a battered billycock but as an intellectual +problem. And, first, as to how it came here. It arrived upon +Christmas morning, in company with a good fat goose, which is, I +have no doubt, roasting at this moment in front of Peterson's +fire. The facts are these: about four o'clock on Christmas +morning, Peterson, who, as you know, is a very honest fellow, was +returning from some small jollification and was making his way +homeward down Tottenham Court Road. In front of him he saw, in +the gaslight, a tallish man, walking with a slight stagger, and +carrying a white goose slung over his shoulder. As he reached the +corner of Goodge Street, a row broke out between this stranger +and a little knot of roughs. One of the latter knocked off the +man's hat, on which he raised his stick to defend himself and, +swinging it over his head, smashed the shop window behind him. +Peterson had rushed forward to protect the stranger from his +assailants; but the man, shocked at having broken the window, and +seeing an official-looking person in uniform rushing towards him, +dropped his goose, took to his heels, and vanished amid the +labyrinth of small streets which lie at the back of Tottenham +Court Road. The roughs had also fled at the appearance of +Peterson, so that he was left in possession of the field of +battle, and also of the spoils of victory in the shape of this +battered hat and a most unimpeachable Christmas goose." + +"Which surely he restored to their owner?" + +"My dear fellow, there lies the problem. It is true that 'For +Mrs. Henry Baker' was printed upon a small card which was tied to +the bird's left leg, and it is also true that the initials 'H. +B.' are legible upon the lining of this hat, but as there are +some thousands of Bakers, and some hundreds of Henry Bakers in +this city of ours, it is not easy to restore lost property to any +one of them." + +"What, then, did Peterson do?" + +"He brought round both hat and goose to me on Christmas morning, +knowing that even the smallest problems are of interest to me. +The goose we retained until this morning, when there were signs +that, in spite of the slight frost, it would be well that it +should be eaten without unnecessary delay. Its finder has carried +it off, therefore, to fulfil the ultimate destiny of a goose, +while I continue to retain the hat of the unknown gentleman who +lost his Christmas dinner." + +"Did he not advertise?" + +"No." + +"Then, what clue could you have as to his identity?" + +"Only as much as we can deduce." + +"From his hat?" + +"Precisely." + +"But you are joking. What can you gather from this old battered +felt?" + +"Here is my lens. You know my methods. What can you gather +yourself as to the individuality of the man who has worn this +article?" + +I took the tattered object in my hands and turned it over rather +ruefully. It was a very ordinary black hat of the usual round +shape, hard and much the worse for wear. The lining had been of +red silk, but was a good deal discoloured. There was no maker's +name; but, as Holmes had remarked, the initials "H. B." were +scrawled upon one side. It was pierced in the brim for a +hat-securer, but the elastic was missing. For the rest, it was +cracked, exceedingly dusty, and spotted in several places, +although there seemed to have been some attempt to hide the +discoloured patches by smearing them with ink. + +"I can see nothing," said I, handing it back to my friend. + +"On the contrary, Watson, you can see everything. You fail, +however, to reason from what you see. You are too timid in +drawing your inferences." + +"Then, pray tell me what it is that you can infer from this hat?" + +He picked it up and gazed at it in the peculiar introspective +fashion which was characteristic of him. "It is perhaps less +suggestive than it might have been," he remarked, "and yet there +are a few inferences which are very distinct, and a few others +which represent at least a strong balance of probability. That +the man was highly intellectual is of course obvious upon the +face of it, and also that he was fairly well-to-do within the +last three years, although he has now fallen upon evil days. He +had foresight, but has less now than formerly, pointing to a +moral retrogression, which, when taken with the decline of his +fortunes, seems to indicate some evil influence, probably drink, +at work upon him. This may account also for the obvious fact that +his wife has ceased to love him." + +"My dear Holmes!" + +"He has, however, retained some degree of self-respect," he +continued, disregarding my remonstrance. "He is a man who leads a +sedentary life, goes out little, is out of training entirely, is +middle-aged, has grizzled hair which he has had cut within the +last few days, and which he anoints with lime-cream. These are +the more patent facts which are to be deduced from his hat. Also, +by the way, that it is extremely improbable that he has gas laid +on in his house." + +"You are certainly joking, Holmes." + +"Not in the least. Is it possible that even now, when I give you +these results, you are unable to see how they are attained?" + +"I have no doubt that I am very stupid, but I must confess that I +am unable to follow you. For example, how did you deduce that +this man was intellectual?" + +For answer Holmes clapped the hat upon his head. It came right +over the forehead and settled upon the bridge of his nose. "It is +a question of cubic capacity," said he; "a man with so large a +brain must have something in it." + +"The decline of his fortunes, then?" + +"This hat is three years old. These flat brims curled at the edge +came in then. It is a hat of the very best quality. Look at the +band of ribbed silk and the excellent lining. If this man could +afford to buy so expensive a hat three years ago, and has had no +hat since, then he has assuredly gone down in the world." + +"Well, that is clear enough, certainly. But how about the +foresight and the moral retrogression?" + +Sherlock Holmes laughed. "Here is the foresight," said he putting +his finger upon the little disc and loop of the hat-securer. +"They are never sold upon hats. If this man ordered one, it is a +sign of a certain amount of foresight, since he went out of his +way to take this precaution against the wind. But since we see +that he has broken the elastic and has not troubled to replace +it, it is obvious that he has less foresight now than formerly, +which is a distinct proof of a weakening nature. On the other +hand, he has endeavoured to conceal some of these stains upon the +felt by daubing them with ink, which is a sign that he has not +entirely lost his self-respect." + +"Your reasoning is certainly plausible." + +"The further points, that he is middle-aged, that his hair is +grizzled, that it has been recently cut, and that he uses +lime-cream, are all to be gathered from a close examination of the +lower part of the lining. The lens discloses a large number of +hair-ends, clean cut by the scissors of the barber. They all +appear to be adhesive, and there is a distinct odour of +lime-cream. This dust, you will observe, is not the gritty, grey +dust of the street but the fluffy brown dust of the house, +showing that it has been hung up indoors most of the time, while +the marks of moisture upon the inside are proof positive that the +wearer perspired very freely, and could therefore, hardly be in +the best of training." + +"But his wife--you said that she had ceased to love him." + +"This hat has not been brushed for weeks. When I see you, my dear +Watson, with a week's accumulation of dust upon your hat, and +when your wife allows you to go out in such a state, I shall fear +that you also have been unfortunate enough to lose your wife's +affection." + +"But he might be a bachelor." + +"Nay, he was bringing home the goose as a peace-offering to his +wife. Remember the card upon the bird's leg." + +"You have an answer to everything. But how on earth do you deduce +that the gas is not laid on in his house?" + +"One tallow stain, or even two, might come by chance; but when I +see no less than five, I think that there can be little doubt +that the individual must be brought into frequent contact with +burning tallow--walks upstairs at night probably with his hat in +one hand and a guttering candle in the other. Anyhow, he never +got tallow-stains from a gas-jet. Are you satisfied?" + +"Well, it is very ingenious," said I, laughing; "but since, as +you said just now, there has been no crime committed, and no harm +done save the loss of a goose, all this seems to be rather a +waste of energy." + +Sherlock Holmes had opened his mouth to reply, when the door flew +open, and Peterson, the commissionaire, rushed into the apartment +with flushed cheeks and the face of a man who is dazed with +astonishment. + +"The goose, Mr. Holmes! The goose, sir!" he gasped. + +"Eh? What of it, then? Has it returned to life and flapped off +through the kitchen window?" Holmes twisted himself round upon +the sofa to get a fairer view of the man's excited face. + +"See here, sir! See what my wife found in its crop!" He held out +his hand and displayed upon the centre of the palm a brilliantly +scintillating blue stone, rather smaller than a bean in size, but +of such purity and radiance that it twinkled like an electric +point in the dark hollow of his hand. + +Sherlock Holmes sat up with a whistle. "By Jove, Peterson!" said +he, "this is treasure trove indeed. I suppose you know what you +have got?" + +"A diamond, sir? A precious stone. It cuts into glass as though +it were putty." + +"It's more than a precious stone. It is the precious stone." + +"Not the Countess of Morcar's blue carbuncle!" I ejaculated. + +"Precisely so. I ought to know its size and shape, seeing that I +have read the advertisement about it in The Times every day +lately. It is absolutely unique, and its value can only be +conjectured, but the reward offered of 1000 pounds is certainly +not within a twentieth part of the market price." + +"A thousand pounds! Great Lord of mercy!" The commissionaire +plumped down into a chair and stared from one to the other of us. + +"That is the reward, and I have reason to know that there are +sentimental considerations in the background which would induce +the Countess to part with half her fortune if she could but +recover the gem." + +"It was lost, if I remember aright, at the Hotel Cosmopolitan," I +remarked. + +"Precisely so, on December 22nd, just five days ago. John Horner, +a plumber, was accused of having abstracted it from the lady's +jewel-case. The evidence against him was so strong that the case +has been referred to the Assizes. I have some account of the +matter here, I believe." He rummaged amid his newspapers, +glancing over the dates, until at last he smoothed one out, +doubled it over, and read the following paragraph: + +"Hotel Cosmopolitan Jewel Robbery. John Horner, 26, plumber, was +brought up upon the charge of having upon the 22nd inst., +abstracted from the jewel-case of the Countess of Morcar the +valuable gem known as the blue carbuncle. James Ryder, +upper-attendant at the hotel, gave his evidence to the effect +that he had shown Horner up to the dressing-room of the Countess +of Morcar upon the day of the robbery in order that he might +solder the second bar of the grate, which was loose. He had +remained with Horner some little time, but had finally been +called away. On returning, he found that Horner had disappeared, +that the bureau had been forced open, and that the small morocco +casket in which, as it afterwards transpired, the Countess was +accustomed to keep her jewel, was lying empty upon the +dressing-table. Ryder instantly gave the alarm, and Horner was +arrested the same evening; but the stone could not be found +either upon his person or in his rooms. Catherine Cusack, maid to +the Countess, deposed to having heard Ryder's cry of dismay on +discovering the robbery, and to having rushed into the room, +where she found matters as described by the last witness. +Inspector Bradstreet, B division, gave evidence as to the arrest +of Horner, who struggled frantically, and protested his innocence +in the strongest terms. Evidence of a previous conviction for +robbery having been given against the prisoner, the magistrate +refused to deal summarily with the offence, but referred it to +the Assizes. Horner, who had shown signs of intense emotion +during the proceedings, fainted away at the conclusion and was +carried out of court." + +"Hum! So much for the police-court," said Holmes thoughtfully, +tossing aside the paper. "The question for us now to solve is the +sequence of events leading from a rifled jewel-case at one end to +the crop of a goose in Tottenham Court Road at the other. You +see, Watson, our little deductions have suddenly assumed a much +more important and less innocent aspect. Here is the stone; the +stone came from the goose, and the goose came from Mr. Henry +Baker, the gentleman with the bad hat and all the other +characteristics with which I have bored you. So now we must set +ourselves very seriously to finding this gentleman and +ascertaining what part he has played in this little mystery. To +do this, we must try the simplest means first, and these lie +undoubtedly in an advertisement in all the evening papers. If +this fail, I shall have recourse to other methods." + +"What will you say?" + +"Give me a pencil and that slip of paper. Now, then: 'Found at +the corner of Goodge Street, a goose and a black felt hat. Mr. +Henry Baker can have the same by applying at 6:30 this evening at +221B, Baker Street.' That is clear and concise." + +"Very. But will he see it?" + +"Well, he is sure to keep an eye on the papers, since, to a poor +man, the loss was a heavy one. He was clearly so scared by his +mischance in breaking the window and by the approach of Peterson +that he thought of nothing but flight, but since then he must +have bitterly regretted the impulse which caused him to drop his +bird. Then, again, the introduction of his name will cause him to +see it, for everyone who knows him will direct his attention to +it. Here you are, Peterson, run down to the advertising agency +and have this put in the evening papers." + +"In which, sir?" + +"Oh, in the Globe, Star, Pall Mall, St. James's, Evening News, +Standard, Echo, and any others that occur to you." + +"Very well, sir. And this stone?" + +"Ah, yes, I shall keep the stone. Thank you. And, I say, +Peterson, just buy a goose on your way back and leave it here +with me, for we must have one to give to this gentleman in place +of the one which your family is now devouring." + +When the commissionaire had gone, Holmes took up the stone and +held it against the light. "It's a bonny thing," said he. "Just +see how it glints and sparkles. Of course it is a nucleus and +focus of crime. Every good stone is. They are the devil's pet +baits. In the larger and older jewels every facet may stand for a +bloody deed. This stone is not yet twenty years old. It was found +in the banks of the Amoy River in southern China and is remarkable +in having every characteristic of the carbuncle, save that it is +blue in shade instead of ruby red. In spite of its youth, it has +already a sinister history. There have been two murders, a +vitriol-throwing, a suicide, and several robberies brought about +for the sake of this forty-grain weight of crystallised charcoal. +Who would think that so pretty a toy would be a purveyor to the +gallows and the prison? I'll lock it up in my strong box now and +drop a line to the Countess to say that we have it." + +"Do you think that this man Horner is innocent?" + +"I cannot tell." + +"Well, then, do you imagine that this other one, Henry Baker, had +anything to do with the matter?" + +"It is, I think, much more likely that Henry Baker is an +absolutely innocent man, who had no idea that the bird which he +was carrying was of considerably more value than if it were made +of solid gold. That, however, I shall determine by a very simple +test if we have an answer to our advertisement." + +"And you can do nothing until then?" + +"Nothing." + +"In that case I shall continue my professional round. But I shall +come back in the evening at the hour you have mentioned, for I +should like to see the solution of so tangled a business." + +"Very glad to see you. I dine at seven. There is a woodcock, I +believe. By the way, in view of recent occurrences, perhaps I +ought to ask Mrs. Hudson to examine its crop." + +I had been delayed at a case, and it was a little after half-past +six when I found myself in Baker Street once more. As I +approached the house I saw a tall man in a Scotch bonnet with a +coat which was buttoned up to his chin waiting outside in the +bright semicircle which was thrown from the fanlight. Just as I +arrived the door was opened, and we were shown up together to +Holmes' room. + +"Mr. Henry Baker, I believe," said he, rising from his armchair +and greeting his visitor with the easy air of geniality which he +could so readily assume. "Pray take this chair by the fire, Mr. +Baker. It is a cold night, and I observe that your circulation is +more adapted for summer than for winter. Ah, Watson, you have +just come at the right time. Is that your hat, Mr. Baker?" + +"Yes, sir, that is undoubtedly my hat." + +He was a large man with rounded shoulders, a massive head, and a +broad, intelligent face, sloping down to a pointed beard of +grizzled brown. A touch of red in nose and cheeks, with a slight +tremor of his extended hand, recalled Holmes' surmise as to his +habits. His rusty black frock-coat was buttoned right up in +front, with the collar turned up, and his lank wrists protruded +from his sleeves without a sign of cuff or shirt. He spoke in a +slow staccato fashion, choosing his words with care, and gave the +impression generally of a man of learning and letters who had had +ill-usage at the hands of fortune. + +"We have retained these things for some days," said Holmes, +"because we expected to see an advertisement from you giving your +address. I am at a loss to know now why you did not advertise." + +Our visitor gave a rather shamefaced laugh. "Shillings have not +been so plentiful with me as they once were," he remarked. "I had +no doubt that the gang of roughs who assaulted me had carried off +both my hat and the bird. I did not care to spend more money in a +hopeless attempt at recovering them." + +"Very naturally. By the way, about the bird, we were compelled to +eat it." + +"To eat it!" Our visitor half rose from his chair in his +excitement. + +"Yes, it would have been of no use to anyone had we not done so. +But I presume that this other goose upon the sideboard, which is +about the same weight and perfectly fresh, will answer your +purpose equally well?" + +"Oh, certainly, certainly," answered Mr. Baker with a sigh of +relief. + +"Of course, we still have the feathers, legs, crop, and so on of +your own bird, so if you wish--" + +The man burst into a hearty laugh. "They might be useful to me as +relics of my adventure," said he, "but beyond that I can hardly +see what use the disjecta membra of my late acquaintance are +going to be to me. No, sir, I think that, with your permission, I +will confine my attentions to the excellent bird which I perceive +upon the sideboard." + +Sherlock Holmes glanced sharply across at me with a slight shrug +of his shoulders. + +"There is your hat, then, and there your bird," said he. "By the +way, would it bore you to tell me where you got the other one +from? I am somewhat of a fowl fancier, and I have seldom seen a +better grown goose." + +"Certainly, sir," said Baker, who had risen and tucked his newly +gained property under his arm. "There are a few of us who +frequent the Alpha Inn, near the Museum--we are to be found in +the Museum itself during the day, you understand. This year our +good host, Windigate by name, instituted a goose club, by which, +on consideration of some few pence every week, we were each to +receive a bird at Christmas. My pence were duly paid, and the +rest is familiar to you. I am much indebted to you, sir, for a +Scotch bonnet is fitted neither to my years nor my gravity." With +a comical pomposity of manner he bowed solemnly to both of us and +strode off upon his way. + +"So much for Mr. Henry Baker," said Holmes when he had closed the +door behind him. "It is quite certain that he knows nothing +whatever about the matter. Are you hungry, Watson?" + +"Not particularly." + +"Then I suggest that we turn our dinner into a supper and follow +up this clue while it is still hot." + +"By all means." + +It was a bitter night, so we drew on our ulsters and wrapped +cravats about our throats. Outside, the stars were shining coldly +in a cloudless sky, and the breath of the passers-by blew out +into smoke like so many pistol shots. Our footfalls rang out +crisply and loudly as we swung through the doctors' quarter, +Wimpole Street, Harley Street, and so through Wigmore Street into +Oxford Street. In a quarter of an hour we were in Bloomsbury at +the Alpha Inn, which is a small public-house at the corner of one +of the streets which runs down into Holborn. Holmes pushed open +the door of the private bar and ordered two glasses of beer from +the ruddy-faced, white-aproned landlord. + +"Your beer should be excellent if it is as good as your geese," +said he. + +"My geese!" The man seemed surprised. + +"Yes. I was speaking only half an hour ago to Mr. Henry Baker, +who was a member of your goose club." + +"Ah! yes, I see. But you see, sir, them's not our geese." + +"Indeed! Whose, then?" + +"Well, I got the two dozen from a salesman in Covent Garden." + +"Indeed? I know some of them. Which was it?" + +"Breckinridge is his name." + +"Ah! I don't know him. Well, here's your good health landlord, +and prosperity to your house. Good-night." + +"Now for Mr. Breckinridge," he continued, buttoning up his coat +as we came out into the frosty air. "Remember, Watson that though +we have so homely a thing as a goose at one end of this chain, we +have at the other a man who will certainly get seven years' penal +servitude unless we can establish his innocence. It is possible +that our inquiry may but confirm his guilt; but, in any case, we +have a line of investigation which has been missed by the police, +and which a singular chance has placed in our hands. Let us +follow it out to the bitter end. Faces to the south, then, and +quick march!" + +We passed across Holborn, down Endell Street, and so through a +zigzag of slums to Covent Garden Market. One of the largest +stalls bore the name of Breckinridge upon it, and the proprietor +a horsey-looking man, with a sharp face and trim side-whiskers was +helping a boy to put up the shutters. + +"Good-evening. It's a cold night," said Holmes. + +The salesman nodded and shot a questioning glance at my +companion. + +"Sold out of geese, I see," continued Holmes, pointing at the +bare slabs of marble. + +"Let you have five hundred to-morrow morning." + +"That's no good." + +"Well, there are some on the stall with the gas-flare." + +"Ah, but I was recommended to you." + +"Who by?" + +"The landlord of the Alpha." + +"Oh, yes; I sent him a couple of dozen." + +"Fine birds they were, too. Now where did you get them from?" + +To my surprise the question provoked a burst of anger from the +salesman. + +"Now, then, mister," said he, with his head cocked and his arms +akimbo, "what are you driving at? Let's have it straight, now." + +"It is straight enough. I should like to know who sold you the +geese which you supplied to the Alpha." + +"Well then, I shan't tell you. So now!" + +"Oh, it is a matter of no importance; but I don't know why you +should be so warm over such a trifle." + +"Warm! You'd be as warm, maybe, if you were as pestered as I am. +When I pay good money for a good article there should be an end +of the business; but it's 'Where are the geese?' and 'Who did you +sell the geese to?' and 'What will you take for the geese?' One +would think they were the only geese in the world, to hear the +fuss that is made over them." + +"Well, I have no connection with any other people who have been +making inquiries," said Holmes carelessly. "If you won't tell us +the bet is off, that is all. But I'm always ready to back my +opinion on a matter of fowls, and I have a fiver on it that the +bird I ate is country bred." + +"Well, then, you've lost your fiver, for it's town bred," snapped +the salesman. + +"It's nothing of the kind." + +"I say it is." + +"I don't believe it." + +"D'you think you know more about fowls than I, who have handled +them ever since I was a nipper? I tell you, all those birds that +went to the Alpha were town bred." + +"You'll never persuade me to believe that." + +"Will you bet, then?" + +"It's merely taking your money, for I know that I am right. But +I'll have a sovereign on with you, just to teach you not to be +obstinate." + +The salesman chuckled grimly. "Bring me the books, Bill," said +he. + +The small boy brought round a small thin volume and a great +greasy-backed one, laying them out together beneath the hanging +lamp. + +"Now then, Mr. Cocksure," said the salesman, "I thought that I +was out of geese, but before I finish you'll find that there is +still one left in my shop. You see this little book?" + +"Well?" + +"That's the list of the folk from whom I buy. D'you see? Well, +then, here on this page are the country folk, and the numbers +after their names are where their accounts are in the big ledger. +Now, then! You see this other page in red ink? Well, that is a +list of my town suppliers. Now, look at that third name. Just +read it out to me." + +"Mrs. Oakshott, 117, Brixton Road--249," read Holmes. + +"Quite so. Now turn that up in the ledger." + +Holmes turned to the page indicated. "Here you are, 'Mrs. +Oakshott, 117, Brixton Road, egg and poultry supplier.'" + +"Now, then, what's the last entry?" + +"'December 22nd. Twenty-four geese at 7s. 6d.'" + +"Quite so. There you are. And underneath?" + +"'Sold to Mr. Windigate of the Alpha, at 12s.'" + +"What have you to say now?" + +Sherlock Holmes looked deeply chagrined. He drew a sovereign from +his pocket and threw it down upon the slab, turning away with the +air of a man whose disgust is too deep for words. A few yards off +he stopped under a lamp-post and laughed in the hearty, noiseless +fashion which was peculiar to him. + +"When you see a man with whiskers of that cut and the 'Pink 'un' +protruding out of his pocket, you can always draw him by a bet," +said he. "I daresay that if I had put 100 pounds down in front of +him, that man would not have given me such complete information +as was drawn from him by the idea that he was doing me on a +wager. Well, Watson, we are, I fancy, nearing the end of our +quest, and the only point which remains to be determined is +whether we should go on to this Mrs. Oakshott to-night, or +whether we should reserve it for to-morrow. It is clear from what +that surly fellow said that there are others besides ourselves +who are anxious about the matter, and I should--" + +His remarks were suddenly cut short by a loud hubbub which broke +out from the stall which we had just left. Turning round we saw a +little rat-faced fellow standing in the centre of the circle of +yellow light which was thrown by the swinging lamp, while +Breckinridge, the salesman, framed in the door of his stall, was +shaking his fists fiercely at the cringing figure. + +"I've had enough of you and your geese," he shouted. "I wish you +were all at the devil together. If you come pestering me any more +with your silly talk I'll set the dog at you. You bring Mrs. +Oakshott here and I'll answer her, but what have you to do with +it? Did I buy the geese off you?" + +"No; but one of them was mine all the same," whined the little +man. + +"Well, then, ask Mrs. Oakshott for it." + +"She told me to ask you." + +"Well, you can ask the King of Proosia, for all I care. I've had +enough of it. Get out of this!" He rushed fiercely forward, and +the inquirer flitted away into the darkness. + +"Ha! this may save us a visit to Brixton Road," whispered Holmes. +"Come with me, and we will see what is to be made of this +fellow." Striding through the scattered knots of people who +lounged round the flaring stalls, my companion speedily overtook +the little man and touched him upon the shoulder. He sprang +round, and I could see in the gas-light that every vestige of +colour had been driven from his face. + +"Who are you, then? What do you want?" he asked in a quavering +voice. + +"You will excuse me," said Holmes blandly, "but I could not help +overhearing the questions which you put to the salesman just now. +I think that I could be of assistance to you." + +"You? Who are you? How could you know anything of the matter?" + +"My name is Sherlock Holmes. It is my business to know what other +people don't know." + +"But you can know nothing of this?" + +"Excuse me, I know everything of it. You are endeavouring to +trace some geese which were sold by Mrs. Oakshott, of Brixton +Road, to a salesman named Breckinridge, by him in turn to Mr. +Windigate, of the Alpha, and by him to his club, of which Mr. +Henry Baker is a member." + +"Oh, sir, you are the very man whom I have longed to meet," cried +the little fellow with outstretched hands and quivering fingers. +"I can hardly explain to you how interested I am in this matter." + +Sherlock Holmes hailed a four-wheeler which was passing. "In that +case we had better discuss it in a cosy room rather than in this +wind-swept market-place," said he. "But pray tell me, before we +go farther, who it is that I have the pleasure of assisting." + +The man hesitated for an instant. "My name is John Robinson," he +answered with a sidelong glance. + +"No, no; the real name," said Holmes sweetly. "It is always +awkward doing business with an alias." + +A flush sprang to the white cheeks of the stranger. "Well then," +said he, "my real name is James Ryder." + +"Precisely so. Head attendant at the Hotel Cosmopolitan. Pray +step into the cab, and I shall soon be able to tell you +everything which you would wish to know." + +The little man stood glancing from one to the other of us with +half-frightened, half-hopeful eyes, as one who is not sure +whether he is on the verge of a windfall or of a catastrophe. +Then he stepped into the cab, and in half an hour we were back in +the sitting-room at Baker Street. Nothing had been said during +our drive, but the high, thin breathing of our new companion, and +the claspings and unclaspings of his hands, spoke of the nervous +tension within him. + +"Here we are!" said Holmes cheerily as we filed into the room. +"The fire looks very seasonable in this weather. You look cold, +Mr. Ryder. Pray take the basket-chair. I will just put on my +slippers before we settle this little matter of yours. Now, then! +You want to know what became of those geese?" + +"Yes, sir." + +"Or rather, I fancy, of that goose. It was one bird, I imagine in +which you were interested--white, with a black bar across the +tail." + +Ryder quivered with emotion. "Oh, sir," he cried, "can you tell +me where it went to?" + +"It came here." + +"Here?" + +"Yes, and a most remarkable bird it proved. I don't wonder that +you should take an interest in it. It laid an egg after it was +dead--the bonniest, brightest little blue egg that ever was seen. +I have it here in my museum." + +Our visitor staggered to his feet and clutched the mantelpiece +with his right hand. Holmes unlocked his strong-box and held up +the blue carbuncle, which shone out like a star, with a cold, +brilliant, many-pointed radiance. Ryder stood glaring with a +drawn face, uncertain whether to claim or to disown it. + +"The game's up, Ryder," said Holmes quietly. "Hold up, man, or +you'll be into the fire! Give him an arm back into his chair, +Watson. He's not got blood enough to go in for felony with +impunity. Give him a dash of brandy. So! Now he looks a little +more human. What a shrimp it is, to be sure!" + +For a moment he had staggered and nearly fallen, but the brandy +brought a tinge of colour into his cheeks, and he sat staring +with frightened eyes at his accuser. + +"I have almost every link in my hands, and all the proofs which I +could possibly need, so there is little which you need tell me. +Still, that little may as well be cleared up to make the case +complete. You had heard, Ryder, of this blue stone of the +Countess of Morcar's?" + +"It was Catherine Cusack who told me of it," said he in a +crackling voice. + +"I see--her ladyship's waiting-maid. Well, the temptation of +sudden wealth so easily acquired was too much for you, as it has +been for better men before you; but you were not very scrupulous +in the means you used. It seems to me, Ryder, that there is the +making of a very pretty villain in you. You knew that this man +Horner, the plumber, had been concerned in some such matter +before, and that suspicion would rest the more readily upon him. +What did you do, then? You made some small job in my lady's +room--you and your confederate Cusack--and you managed that he +should be the man sent for. Then, when he had left, you rifled +the jewel-case, raised the alarm, and had this unfortunate man +arrested. You then--" + +Ryder threw himself down suddenly upon the rug and clutched at my +companion's knees. "For God's sake, have mercy!" he shrieked. +"Think of my father! Of my mother! It would break their hearts. I +never went wrong before! I never will again. I swear it. I'll +swear it on a Bible. Oh, don't bring it into court! For Christ's +sake, don't!" + +"Get back into your chair!" said Holmes sternly. "It is very well +to cringe and crawl now, but you thought little enough of this +poor Horner in the dock for a crime of which he knew nothing." + +"I will fly, Mr. Holmes. I will leave the country, sir. Then the +charge against him will break down." + +"Hum! We will talk about that. And now let us hear a true account +of the next act. How came the stone into the goose, and how came +the goose into the open market? Tell us the truth, for there lies +your only hope of safety." + +Ryder passed his tongue over his parched lips. "I will tell you +it just as it happened, sir," said he. "When Horner had been +arrested, it seemed to me that it would be best for me to get +away with the stone at once, for I did not know at what moment +the police might not take it into their heads to search me and my +room. There was no place about the hotel where it would be safe. +I went out, as if on some commission, and I made for my sister's +house. She had married a man named Oakshott, and lived in Brixton +Road, where she fattened fowls for the market. All the way there +every man I met seemed to me to be a policeman or a detective; +and, for all that it was a cold night, the sweat was pouring down +my face before I came to the Brixton Road. My sister asked me +what was the matter, and why I was so pale; but I told her that I +had been upset by the jewel robbery at the hotel. Then I went +into the back yard and smoked a pipe and wondered what it would +be best to do. + +"I had a friend once called Maudsley, who went to the bad, and +has just been serving his time in Pentonville. One day he had met +me, and fell into talk about the ways of thieves, and how they +could get rid of what they stole. I knew that he would be true to +me, for I knew one or two things about him; so I made up my mind +to go right on to Kilburn, where he lived, and take him into my +confidence. He would show me how to turn the stone into money. +But how to get to him in safety? I thought of the agonies I had +gone through in coming from the hotel. I might at any moment be +seized and searched, and there would be the stone in my waistcoat +pocket. I was leaning against the wall at the time and looking at +the geese which were waddling about round my feet, and suddenly +an idea came into my head which showed me how I could beat the +best detective that ever lived. + +"My sister had told me some weeks before that I might have the +pick of her geese for a Christmas present, and I knew that she +was always as good as her word. I would take my goose now, and in +it I would carry my stone to Kilburn. There was a little shed in +the yard, and behind this I drove one of the birds--a fine big +one, white, with a barred tail. I caught it, and prying its bill +open, I thrust the stone down its throat as far as my finger +could reach. The bird gave a gulp, and I felt the stone pass +along its gullet and down into its crop. But the creature flapped +and struggled, and out came my sister to know what was the +matter. As I turned to speak to her the brute broke loose and +fluttered off among the others. + +"'Whatever were you doing with that bird, Jem?' says she. + +"'Well,' said I, 'you said you'd give me one for Christmas, and I +was feeling which was the fattest.' + +"'Oh,' says she, 'we've set yours aside for you--Jem's bird, we +call it. It's the big white one over yonder. There's twenty-six +of them, which makes one for you, and one for us, and two dozen +for the market.' + +"'Thank you, Maggie,' says I; 'but if it is all the same to you, +I'd rather have that one I was handling just now.' + +"'The other is a good three pound heavier,' said she, 'and we +fattened it expressly for you.' + +"'Never mind. I'll have the other, and I'll take it now,' said I. + +"'Oh, just as you like,' said she, a little huffed. 'Which is it +you want, then?' + +"'That white one with the barred tail, right in the middle of the +flock.' + +"'Oh, very well. Kill it and take it with you.' + +"Well, I did what she said, Mr. Holmes, and I carried the bird +all the way to Kilburn. I told my pal what I had done, for he was +a man that it was easy to tell a thing like that to. He laughed +until he choked, and we got a knife and opened the goose. My +heart turned to water, for there was no sign of the stone, and I +knew that some terrible mistake had occurred. I left the bird, +rushed back to my sister's, and hurried into the back yard. There +was not a bird to be seen there. + +"'Where are they all, Maggie?' I cried. + +"'Gone to the dealer's, Jem.' + +"'Which dealer's?' + +"'Breckinridge, of Covent Garden.' + +"'But was there another with a barred tail?' I asked, 'the same +as the one I chose?' + +"'Yes, Jem; there were two barred-tailed ones, and I could never +tell them apart.' + +"Well, then, of course I saw it all, and I ran off as hard as my +feet would carry me to this man Breckinridge; but he had sold the +lot at once, and not one word would he tell me as to where they +had gone. You heard him yourselves to-night. Well, he has always +answered me like that. My sister thinks that I am going mad. +Sometimes I think that I am myself. And now--and now I am myself +a branded thief, without ever having touched the wealth for which +I sold my character. God help me! God help me!" He burst into +convulsive sobbing, with his face buried in his hands. + +There was a long silence, broken only by his heavy breathing and +by the measured tapping of Sherlock Holmes' finger-tips upon the +edge of the table. Then my friend rose and threw open the door. + +"Get out!" said he. + +"What, sir! Oh, Heaven bless you!" + +"No more words. Get out!" + +And no more words were needed. There was a rush, a clatter upon +the stairs, the bang of a door, and the crisp rattle of running +footfalls from the street. + +"After all, Watson," said Holmes, reaching up his hand for his +clay pipe, "I am not retained by the police to supply their +deficiencies. If Horner were in danger it would be another thing; +but this fellow will not appear against him, and the case must +collapse. I suppose that I am commuting a felony, but it is just +possible that I am saving a soul. This fellow will not go wrong +again; he is too terribly frightened. Send him to gaol now, and +you make him a gaol-bird for life. Besides, it is the season of +forgiveness. Chance has put in our way a most singular and +whimsical problem, and its solution is its own reward. If you +will have the goodness to touch the bell, Doctor, we will begin +another investigation, in which, also a bird will be the chief +feature." + + + +VIII. THE ADVENTURE OF THE SPECKLED BAND + +On glancing over my notes of the seventy odd cases in which I +have during the last eight years studied the methods of my friend +Sherlock Holmes, I find many tragic, some comic, a large number +merely strange, but none commonplace; for, working as he did +rather for the love of his art than for the acquirement of +wealth, he refused to associate himself with any investigation +which did not tend towards the unusual, and even the fantastic. +Of all these varied cases, however, I cannot recall any which +presented more singular features than that which was associated +with the well-known Surrey family of the Roylotts of Stoke Moran. +The events in question occurred in the early days of my +association with Holmes, when we were sharing rooms as bachelors +in Baker Street. It is possible that I might have placed them +upon record before, but a promise of secrecy was made at the +time, from which I have only been freed during the last month by +the untimely death of the lady to whom the pledge was given. It +is perhaps as well that the facts should now come to light, for I +have reasons to know that there are widespread rumours as to the +death of Dr. Grimesby Roylott which tend to make the matter even +more terrible than the truth. + +It was early in April in the year '83 that I woke one morning to +find Sherlock Holmes standing, fully dressed, by the side of my +bed. He was a late riser, as a rule, and as the clock on the +mantelpiece showed me that it was only a quarter-past seven, I +blinked up at him in some surprise, and perhaps just a little +resentment, for I was myself regular in my habits. + +"Very sorry to knock you up, Watson," said he, "but it's the +common lot this morning. Mrs. Hudson has been knocked up, she +retorted upon me, and I on you." + +"What is it, then--a fire?" + +"No; a client. It seems that a young lady has arrived in a +considerable state of excitement, who insists upon seeing me. She +is waiting now in the sitting-room. Now, when young ladies wander +about the metropolis at this hour of the morning, and knock +sleepy people up out of their beds, I presume that it is +something very pressing which they have to communicate. Should it +prove to be an interesting case, you would, I am sure, wish to +follow it from the outset. I thought, at any rate, that I should +call you and give you the chance." + +"My dear fellow, I would not miss it for anything." + +I had no keener pleasure than in following Holmes in his +professional investigations, and in admiring the rapid +deductions, as swift as intuitions, and yet always founded on a +logical basis with which he unravelled the problems which were +submitted to him. I rapidly threw on my clothes and was ready in +a few minutes to accompany my friend down to the sitting-room. A +lady dressed in black and heavily veiled, who had been sitting in +the window, rose as we entered. + +"Good-morning, madam," said Holmes cheerily. "My name is Sherlock +Holmes. This is my intimate friend and associate, Dr. Watson, +before whom you can speak as freely as before myself. Ha! I am +glad to see that Mrs. Hudson has had the good sense to light the +fire. Pray draw up to it, and I shall order you a cup of hot +coffee, for I observe that you are shivering." + +"It is not cold which makes me shiver," said the woman in a low +voice, changing her seat as requested. + +"What, then?" + +"It is fear, Mr. Holmes. It is terror." She raised her veil as +she spoke, and we could see that she was indeed in a pitiable +state of agitation, her face all drawn and grey, with restless +frightened eyes, like those of some hunted animal. Her features +and figure were those of a woman of thirty, but her hair was shot +with premature grey, and her expression was weary and haggard. +Sherlock Holmes ran her over with one of his quick, +all-comprehensive glances. + +"You must not fear," said he soothingly, bending forward and +patting her forearm. "We shall soon set matters right, I have no +doubt. You have come in by train this morning, I see." + +"You know me, then?" + +"No, but I observe the second half of a return ticket in the palm +of your left glove. You must have started early, and yet you had +a good drive in a dog-cart, along heavy roads, before you reached +the station." + +The lady gave a violent start and stared in bewilderment at my +companion. + +"There is no mystery, my dear madam," said he, smiling. "The left +arm of your jacket is spattered with mud in no less than seven +places. The marks are perfectly fresh. There is no vehicle save a +dog-cart which throws up mud in that way, and then only when you +sit on the left-hand side of the driver." + +"Whatever your reasons may be, you are perfectly correct," said +she. "I started from home before six, reached Leatherhead at +twenty past, and came in by the first train to Waterloo. Sir, I +can stand this strain no longer; I shall go mad if it continues. +I have no one to turn to--none, save only one, who cares for me, +and he, poor fellow, can be of little aid. I have heard of you, +Mr. Holmes; I have heard of you from Mrs. Farintosh, whom you +helped in the hour of her sore need. It was from her that I had +your address. Oh, sir, do you not think that you could help me, +too, and at least throw a little light through the dense darkness +which surrounds me? At present it is out of my power to reward +you for your services, but in a month or six weeks I shall be +married, with the control of my own income, and then at least you +shall not find me ungrateful." + +Holmes turned to his desk and, unlocking it, drew out a small +case-book, which he consulted. + +"Farintosh," said he. "Ah yes, I recall the case; it was +concerned with an opal tiara. I think it was before your time, +Watson. I can only say, madam, that I shall be happy to devote +the same care to your case as I did to that of your friend. As to +reward, my profession is its own reward; but you are at liberty +to defray whatever expenses I may be put to, at the time which +suits you best. And now I beg that you will lay before us +everything that may help us in forming an opinion upon the +matter." + +"Alas!" replied our visitor, "the very horror of my situation +lies in the fact that my fears are so vague, and my suspicions +depend so entirely upon small points, which might seem trivial to +another, that even he to whom of all others I have a right to +look for help and advice looks upon all that I tell him about it +as the fancies of a nervous woman. He does not say so, but I can +read it from his soothing answers and averted eyes. But I have +heard, Mr. Holmes, that you can see deeply into the manifold +wickedness of the human heart. You may advise me how to walk amid +the dangers which encompass me." + +"I am all attention, madam." + +"My name is Helen Stoner, and I am living with my stepfather, who +is the last survivor of one of the oldest Saxon families in +England, the Roylotts of Stoke Moran, on the western border of +Surrey." + +Holmes nodded his head. "The name is familiar to me," said he. + +"The family was at one time among the richest in England, and the +estates extended over the borders into Berkshire in the north, +and Hampshire in the west. In the last century, however, four +successive heirs were of a dissolute and wasteful disposition, +and the family ruin was eventually completed by a gambler in the +days of the Regency. Nothing was left save a few acres of ground, +and the two-hundred-year-old house, which is itself crushed under +a heavy mortgage. The last squire dragged out his existence +there, living the horrible life of an aristocratic pauper; but +his only son, my stepfather, seeing that he must adapt himself to +the new conditions, obtained an advance from a relative, which +enabled him to take a medical degree and went out to Calcutta, +where, by his professional skill and his force of character, he +established a large practice. In a fit of anger, however, caused +by some robberies which had been perpetrated in the house, he +beat his native butler to death and narrowly escaped a capital +sentence. As it was, he suffered a long term of imprisonment and +afterwards returned to England a morose and disappointed man. + +"When Dr. Roylott was in India he married my mother, Mrs. Stoner, +the young widow of Major-General Stoner, of the Bengal Artillery. +My sister Julia and I were twins, and we were only two years old +at the time of my mother's re-marriage. She had a considerable +sum of money--not less than 1000 pounds a year--and this she +bequeathed to Dr. Roylott entirely while we resided with him, +with a provision that a certain annual sum should be allowed to +each of us in the event of our marriage. Shortly after our return +to England my mother died--she was killed eight years ago in a +railway accident near Crewe. Dr. Roylott then abandoned his +attempts to establish himself in practice in London and took us +to live with him in the old ancestral house at Stoke Moran. The +money which my mother had left was enough for all our wants, and +there seemed to be no obstacle to our happiness. + +"But a terrible change came over our stepfather about this time. +Instead of making friends and exchanging visits with our +neighbours, who had at first been overjoyed to see a Roylott of +Stoke Moran back in the old family seat, he shut himself up in +his house and seldom came out save to indulge in ferocious +quarrels with whoever might cross his path. Violence of temper +approaching to mania has been hereditary in the men of the +family, and in my stepfather's case it had, I believe, been +intensified by his long residence in the tropics. A series of +disgraceful brawls took place, two of which ended in the +police-court, until at last he became the terror of the village, +and the folks would fly at his approach, for he is a man of +immense strength, and absolutely uncontrollable in his anger. + +"Last week he hurled the local blacksmith over a parapet into a +stream, and it was only by paying over all the money which I +could gather together that I was able to avert another public +exposure. He had no friends at all save the wandering gipsies, +and he would give these vagabonds leave to encamp upon the few +acres of bramble-covered land which represent the family estate, +and would accept in return the hospitality of their tents, +wandering away with them sometimes for weeks on end. He has a +passion also for Indian animals, which are sent over to him by a +correspondent, and he has at this moment a cheetah and a baboon, +which wander freely over his grounds and are feared by the +villagers almost as much as their master. + +"You can imagine from what I say that my poor sister Julia and I +had no great pleasure in our lives. No servant would stay with +us, and for a long time we did all the work of the house. She was +but thirty at the time of her death, and yet her hair had already +begun to whiten, even as mine has." + +"Your sister is dead, then?" + +"She died just two years ago, and it is of her death that I wish +to speak to you. You can understand that, living the life which I +have described, we were little likely to see anyone of our own +age and position. We had, however, an aunt, my mother's maiden +sister, Miss Honoria Westphail, who lives near Harrow, and we +were occasionally allowed to pay short visits at this lady's +house. Julia went there at Christmas two years ago, and met there +a half-pay major of marines, to whom she became engaged. My +stepfather learned of the engagement when my sister returned and +offered no objection to the marriage; but within a fortnight of +the day which had been fixed for the wedding, the terrible event +occurred which has deprived me of my only companion." + +Sherlock Holmes had been leaning back in his chair with his eyes +closed and his head sunk in a cushion, but he half opened his +lids now and glanced across at his visitor. + +"Pray be precise as to details," said he. + +"It is easy for me to be so, for every event of that dreadful +time is seared into my memory. The manor-house is, as I have +already said, very old, and only one wing is now inhabited. The +bedrooms in this wing are on the ground floor, the sitting-rooms +being in the central block of the buildings. Of these bedrooms +the first is Dr. Roylott's, the second my sister's, and the third +my own. There is no communication between them, but they all open +out into the same corridor. Do I make myself plain?" + +"Perfectly so." + +"The windows of the three rooms open out upon the lawn. That +fatal night Dr. Roylott had gone to his room early, though we +knew that he had not retired to rest, for my sister was troubled +by the smell of the strong Indian cigars which it was his custom +to smoke. She left her room, therefore, and came into mine, where +she sat for some time, chatting about her approaching wedding. At +eleven o'clock she rose to leave me, but she paused at the door +and looked back. + +"'Tell me, Helen,' said she, 'have you ever heard anyone whistle +in the dead of the night?' + +"'Never,' said I. + +"'I suppose that you could not possibly whistle, yourself, in +your sleep?' + +"'Certainly not. But why?' + +"'Because during the last few nights I have always, about three +in the morning, heard a low, clear whistle. I am a light sleeper, +and it has awakened me. I cannot tell where it came from--perhaps +from the next room, perhaps from the lawn. I thought that I would +just ask you whether you had heard it.' + +"'No, I have not. It must be those wretched gipsies in the +plantation.' + +"'Very likely. And yet if it were on the lawn, I wonder that you +did not hear it also.' + +"'Ah, but I sleep more heavily than you.' + +"'Well, it is of no great consequence, at any rate.' She smiled +back at me, closed my door, and a few moments later I heard her +key turn in the lock." + +"Indeed," said Holmes. "Was it your custom always to lock +yourselves in at night?" + +"Always." + +"And why?" + +"I think that I mentioned to you that the doctor kept a cheetah +and a baboon. We had no feeling of security unless our doors were +locked." + +"Quite so. Pray proceed with your statement." + +"I could not sleep that night. A vague feeling of impending +misfortune impressed me. My sister and I, you will recollect, +were twins, and you know how subtle are the links which bind two +souls which are so closely allied. It was a wild night. The wind +was howling outside, and the rain was beating and splashing +against the windows. Suddenly, amid all the hubbub of the gale, +there burst forth the wild scream of a terrified woman. I knew +that it was my sister's voice. I sprang from my bed, wrapped a +shawl round me, and rushed into the corridor. As I opened my door +I seemed to hear a low whistle, such as my sister described, and +a few moments later a clanging sound, as if a mass of metal had +fallen. As I ran down the passage, my sister's door was unlocked, +and revolved slowly upon its hinges. I stared at it +horror-stricken, not knowing what was about to issue from it. By +the light of the corridor-lamp I saw my sister appear at the +opening, her face blanched with terror, her hands groping for +help, her whole figure swaying to and fro like that of a +drunkard. I ran to her and threw my arms round her, but at that +moment her knees seemed to give way and she fell to the ground. +She writhed as one who is in terrible pain, and her limbs were +dreadfully convulsed. At first I thought that she had not +recognised me, but as I bent over her she suddenly shrieked out +in a voice which I shall never forget, 'Oh, my God! Helen! It was +the band! The speckled band!' There was something else which she +would fain have said, and she stabbed with her finger into the +air in the direction of the doctor's room, but a fresh convulsion +seized her and choked her words. I rushed out, calling loudly for +my stepfather, and I met him hastening from his room in his +dressing-gown. When he reached my sister's side she was +unconscious, and though he poured brandy down her throat and sent +for medical aid from the village, all efforts were in vain, for +she slowly sank and died without having recovered her +consciousness. Such was the dreadful end of my beloved sister." + +"One moment," said Holmes, "are you sure about this whistle and +metallic sound? Could you swear to it?" + +"That was what the county coroner asked me at the inquiry. It is +my strong impression that I heard it, and yet, among the crash of +the gale and the creaking of an old house, I may possibly have +been deceived." + +"Was your sister dressed?" + +"No, she was in her night-dress. In her right hand was found the +charred stump of a match, and in her left a match-box." + +"Showing that she had struck a light and looked about her when +the alarm took place. That is important. And what conclusions did +the coroner come to?" + +"He investigated the case with great care, for Dr. Roylott's +conduct had long been notorious in the county, but he was unable +to find any satisfactory cause of death. My evidence showed that +the door had been fastened upon the inner side, and the windows +were blocked by old-fashioned shutters with broad iron bars, +which were secured every night. The walls were carefully sounded, +and were shown to be quite solid all round, and the flooring was +also thoroughly examined, with the same result. The chimney is +wide, but is barred up by four large staples. It is certain, +therefore, that my sister was quite alone when she met her end. +Besides, there were no marks of any violence upon her." + +"How about poison?" + +"The doctors examined her for it, but without success." + +"What do you think that this unfortunate lady died of, then?" + +"It is my belief that she died of pure fear and nervous shock, +though what it was that frightened her I cannot imagine." + +"Were there gipsies in the plantation at the time?" + +"Yes, there are nearly always some there." + +"Ah, and what did you gather from this allusion to a band--a +speckled band?" + +"Sometimes I have thought that it was merely the wild talk of +delirium, sometimes that it may have referred to some band of +people, perhaps to these very gipsies in the plantation. I do not +know whether the spotted handkerchiefs which so many of them wear +over their heads might have suggested the strange adjective which +she used." + +Holmes shook his head like a man who is far from being satisfied. + +"These are very deep waters," said he; "pray go on with your +narrative." + +"Two years have passed since then, and my life has been until +lately lonelier than ever. A month ago, however, a dear friend, +whom I have known for many years, has done me the honour to ask +my hand in marriage. His name is Armitage--Percy Armitage--the +second son of Mr. Armitage, of Crane Water, near Reading. My +stepfather has offered no opposition to the match, and we are to +be married in the course of the spring. Two days ago some repairs +were started in the west wing of the building, and my bedroom +wall has been pierced, so that I have had to move into the +chamber in which my sister died, and to sleep in the very bed in +which she slept. Imagine, then, my thrill of terror when last +night, as I lay awake, thinking over her terrible fate, I +suddenly heard in the silence of the night the low whistle which +had been the herald of her own death. I sprang up and lit the +lamp, but nothing was to be seen in the room. I was too shaken to +go to bed again, however, so I dressed, and as soon as it was +daylight I slipped down, got a dog-cart at the Crown Inn, which +is opposite, and drove to Leatherhead, from whence I have come on +this morning with the one object of seeing you and asking your +advice." + +"You have done wisely," said my friend. "But have you told me +all?" + +"Yes, all." + +"Miss Roylott, you have not. You are screening your stepfather." + +"Why, what do you mean?" + +For answer Holmes pushed back the frill of black lace which +fringed the hand that lay upon our visitor's knee. Five little +livid spots, the marks of four fingers and a thumb, were printed +upon the white wrist. + +"You have been cruelly used," said Holmes. + +The lady coloured deeply and covered over her injured wrist. "He +is a hard man," she said, "and perhaps he hardly knows his own +strength." + +There was a long silence, during which Holmes leaned his chin +upon his hands and stared into the crackling fire. + +"This is a very deep business," he said at last. "There are a +thousand details which I should desire to know before I decide +upon our course of action. Yet we have not a moment to lose. If +we were to come to Stoke Moran to-day, would it be possible for +us to see over these rooms without the knowledge of your +stepfather?" + +"As it happens, he spoke of coming into town to-day upon some +most important business. It is probable that he will be away all +day, and that there would be nothing to disturb you. We have a +housekeeper now, but she is old and foolish, and I could easily +get her out of the way." + +"Excellent. You are not averse to this trip, Watson?" + +"By no means." + +"Then we shall both come. What are you going to do yourself?" + +"I have one or two things which I would wish to do now that I am +in town. But I shall return by the twelve o'clock train, so as to +be there in time for your coming." + +"And you may expect us early in the afternoon. I have myself some +small business matters to attend to. Will you not wait and +breakfast?" + +"No, I must go. My heart is lightened already since I have +confided my trouble to you. I shall look forward to seeing you +again this afternoon." She dropped her thick black veil over her +face and glided from the room. + +"And what do you think of it all, Watson?" asked Sherlock Holmes, +leaning back in his chair. + +"It seems to me to be a most dark and sinister business." + +"Dark enough and sinister enough." + +"Yet if the lady is correct in saying that the flooring and walls +are sound, and that the door, window, and chimney are impassable, +then her sister must have been undoubtedly alone when she met her +mysterious end." + +"What becomes, then, of these nocturnal whistles, and what of the +very peculiar words of the dying woman?" + +"I cannot think." + +"When you combine the ideas of whistles at night, the presence of +a band of gipsies who are on intimate terms with this old doctor, +the fact that we have every reason to believe that the doctor has +an interest in preventing his stepdaughter's marriage, the dying +allusion to a band, and, finally, the fact that Miss Helen Stoner +heard a metallic clang, which might have been caused by one of +those metal bars that secured the shutters falling back into its +place, I think that there is good ground to think that the +mystery may be cleared along those lines." + +"But what, then, did the gipsies do?" + +"I cannot imagine." + +"I see many objections to any such theory." + +"And so do I. It is precisely for that reason that we are going +to Stoke Moran this day. I want to see whether the objections are +fatal, or if they may be explained away. But what in the name of +the devil!" + +The ejaculation had been drawn from my companion by the fact that +our door had been suddenly dashed open, and that a huge man had +framed himself in the aperture. His costume was a peculiar +mixture of the professional and of the agricultural, having a +black top-hat, a long frock-coat, and a pair of high gaiters, +with a hunting-crop swinging in his hand. So tall was he that his +hat actually brushed the cross bar of the doorway, and his +breadth seemed to span it across from side to side. A large face, +seared with a thousand wrinkles, burned yellow with the sun, and +marked with every evil passion, was turned from one to the other +of us, while his deep-set, bile-shot eyes, and his high, thin, +fleshless nose, gave him somewhat the resemblance to a fierce old +bird of prey. + +"Which of you is Holmes?" asked this apparition. + +"My name, sir; but you have the advantage of me," said my +companion quietly. + +"I am Dr. Grimesby Roylott, of Stoke Moran." + +"Indeed, Doctor," said Holmes blandly. "Pray take a seat." + +"I will do nothing of the kind. My stepdaughter has been here. I +have traced her. What has she been saying to you?" + +"It is a little cold for the time of the year," said Holmes. + +"What has she been saying to you?" screamed the old man +furiously. + +"But I have heard that the crocuses promise well," continued my +companion imperturbably. + +"Ha! You put me off, do you?" said our new visitor, taking a step +forward and shaking his hunting-crop. "I know you, you scoundrel! +I have heard of you before. You are Holmes, the meddler." + +My friend smiled. + +"Holmes, the busybody!" + +His smile broadened. + +"Holmes, the Scotland Yard Jack-in-office!" + +Holmes chuckled heartily. "Your conversation is most +entertaining," said he. "When you go out close the door, for +there is a decided draught." + +"I will go when I have said my say. Don't you dare to meddle with +my affairs. I know that Miss Stoner has been here. I traced her! +I am a dangerous man to fall foul of! See here." He stepped +swiftly forward, seized the poker, and bent it into a curve with +his huge brown hands. + +"See that you keep yourself out of my grip," he snarled, and +hurling the twisted poker into the fireplace he strode out of the +room. + +"He seems a very amiable person," said Holmes, laughing. "I am +not quite so bulky, but if he had remained I might have shown him +that my grip was not much more feeble than his own." As he spoke +he picked up the steel poker and, with a sudden effort, +straightened it out again. + +"Fancy his having the insolence to confound me with the official +detective force! This incident gives zest to our investigation, +however, and I only trust that our little friend will not suffer +from her imprudence in allowing this brute to trace her. And now, +Watson, we shall order breakfast, and afterwards I shall walk +down to Doctors' Commons, where I hope to get some data which may +help us in this matter." + + +It was nearly one o'clock when Sherlock Holmes returned from his +excursion. He held in his hand a sheet of blue paper, scrawled +over with notes and figures. + +"I have seen the will of the deceased wife," said he. "To +determine its exact meaning I have been obliged to work out the +present prices of the investments with which it is concerned. The +total income, which at the time of the wife's death was little +short of 1100 pounds, is now, through the fall in agricultural +prices, not more than 750 pounds. Each daughter can claim an +income of 250 pounds, in case of marriage. It is evident, +therefore, that if both girls had married, this beauty would have +had a mere pittance, while even one of them would cripple him to +a very serious extent. My morning's work has not been wasted, +since it has proved that he has the very strongest motives for +standing in the way of anything of the sort. And now, Watson, +this is too serious for dawdling, especially as the old man is +aware that we are interesting ourselves in his affairs; so if you +are ready, we shall call a cab and drive to Waterloo. I should be +very much obliged if you would slip your revolver into your +pocket. An Eley's No. 2 is an excellent argument with gentlemen +who can twist steel pokers into knots. That and a tooth-brush +are, I think, all that we need." + +At Waterloo we were fortunate in catching a train for +Leatherhead, where we hired a trap at the station inn and drove +for four or five miles through the lovely Surrey lanes. It was a +perfect day, with a bright sun and a few fleecy clouds in the +heavens. The trees and wayside hedges were just throwing out +their first green shoots, and the air was full of the pleasant +smell of the moist earth. To me at least there was a strange +contrast between the sweet promise of the spring and this +sinister quest upon which we were engaged. My companion sat in +the front of the trap, his arms folded, his hat pulled down over +his eyes, and his chin sunk upon his breast, buried in the +deepest thought. Suddenly, however, he started, tapped me on the +shoulder, and pointed over the meadows. + +"Look there!" said he. + +A heavily timbered park stretched up in a gentle slope, +thickening into a grove at the highest point. From amid the +branches there jutted out the grey gables and high roof-tree of a +very old mansion. + +"Stoke Moran?" said he. + +"Yes, sir, that be the house of Dr. Grimesby Roylott," remarked +the driver. + +"There is some building going on there," said Holmes; "that is +where we are going." + +"There's the village," said the driver, pointing to a cluster of +roofs some distance to the left; "but if you want to get to the +house, you'll find it shorter to get over this stile, and so by +the foot-path over the fields. There it is, where the lady is +walking." + +"And the lady, I fancy, is Miss Stoner," observed Holmes, shading +his eyes. "Yes, I think we had better do as you suggest." + +We got off, paid our fare, and the trap rattled back on its way +to Leatherhead. + +"I thought it as well," said Holmes as we climbed the stile, +"that this fellow should think we had come here as architects, or +on some definite business. It may stop his gossip. +Good-afternoon, Miss Stoner. You see that we have been as good as +our word." + +Our client of the morning had hurried forward to meet us with a +face which spoke her joy. "I have been waiting so eagerly for +you," she cried, shaking hands with us warmly. "All has turned +out splendidly. Dr. Roylott has gone to town, and it is unlikely +that he will be back before evening." + +"We have had the pleasure of making the doctor's acquaintance," +said Holmes, and in a few words he sketched out what had +occurred. Miss Stoner turned white to the lips as she listened. + +"Good heavens!" she cried, "he has followed me, then." + +"So it appears." + +"He is so cunning that I never know when I am safe from him. What +will he say when he returns?" + +"He must guard himself, for he may find that there is someone +more cunning than himself upon his track. You must lock yourself +up from him to-night. If he is violent, we shall take you away to +your aunt's at Harrow. Now, we must make the best use of our +time, so kindly take us at once to the rooms which we are to +examine." + +The building was of grey, lichen-blotched stone, with a high +central portion and two curving wings, like the claws of a crab, +thrown out on each side. In one of these wings the windows were +broken and blocked with wooden boards, while the roof was partly +caved in, a picture of ruin. The central portion was in little +better repair, but the right-hand block was comparatively modern, +and the blinds in the windows, with the blue smoke curling up +from the chimneys, showed that this was where the family resided. +Some scaffolding had been erected against the end wall, and the +stone-work had been broken into, but there were no signs of any +workmen at the moment of our visit. Holmes walked slowly up and +down the ill-trimmed lawn and examined with deep attention the +outsides of the windows. + +"This, I take it, belongs to the room in which you used to sleep, +the centre one to your sister's, and the one next to the main +building to Dr. Roylott's chamber?" + +"Exactly so. But I am now sleeping in the middle one." + +"Pending the alterations, as I understand. By the way, there does +not seem to be any very pressing need for repairs at that end +wall." + +"There were none. I believe that it was an excuse to move me from +my room." + +"Ah! that is suggestive. Now, on the other side of this narrow +wing runs the corridor from which these three rooms open. There +are windows in it, of course?" + +"Yes, but very small ones. Too narrow for anyone to pass +through." + +"As you both locked your doors at night, your rooms were +unapproachable from that side. Now, would you have the kindness +to go into your room and bar your shutters?" + +Miss Stoner did so, and Holmes, after a careful examination +through the open window, endeavoured in every way to force the +shutter open, but without success. There was no slit through +which a knife could be passed to raise the bar. Then with his +lens he tested the hinges, but they were of solid iron, built +firmly into the massive masonry. "Hum!" said he, scratching his +chin in some perplexity, "my theory certainly presents some +difficulties. No one could pass these shutters if they were +bolted. Well, we shall see if the inside throws any light upon +the matter." + +A small side door led into the whitewashed corridor from which +the three bedrooms opened. Holmes refused to examine the third +chamber, so we passed at once to the second, that in which Miss +Stoner was now sleeping, and in which her sister had met with her +fate. It was a homely little room, with a low ceiling and a +gaping fireplace, after the fashion of old country-houses. A +brown chest of drawers stood in one corner, a narrow +white-counterpaned bed in another, and a dressing-table on the +left-hand side of the window. These articles, with two small +wicker-work chairs, made up all the furniture in the room save +for a square of Wilton carpet in the centre. The boards round and +the panelling of the walls were of brown, worm-eaten oak, so old +and discoloured that it may have dated from the original building +of the house. Holmes drew one of the chairs into a corner and sat +silent, while his eyes travelled round and round and up and down, +taking in every detail of the apartment. + +"Where does that bell communicate with?" he asked at last +pointing to a thick bell-rope which hung down beside the bed, the +tassel actually lying upon the pillow. + +"It goes to the housekeeper's room." + +"It looks newer than the other things?" + +"Yes, it was only put there a couple of years ago." + +"Your sister asked for it, I suppose?" + +"No, I never heard of her using it. We used always to get what we +wanted for ourselves." + +"Indeed, it seemed unnecessary to put so nice a bell-pull there. +You will excuse me for a few minutes while I satisfy myself as to +this floor." He threw himself down upon his face with his lens in +his hand and crawled swiftly backward and forward, examining +minutely the cracks between the boards. Then he did the same with +the wood-work with which the chamber was panelled. Finally he +walked over to the bed and spent some time in staring at it and +in running his eye up and down the wall. Finally he took the +bell-rope in his hand and gave it a brisk tug. + +"Why, it's a dummy," said he. + +"Won't it ring?" + +"No, it is not even attached to a wire. This is very interesting. +You can see now that it is fastened to a hook just above where +the little opening for the ventilator is." + +"How very absurd! I never noticed that before." + +"Very strange!" muttered Holmes, pulling at the rope. "There are +one or two very singular points about this room. For example, +what a fool a builder must be to open a ventilator into another +room, when, with the same trouble, he might have communicated +with the outside air!" + +"That is also quite modern," said the lady. + +"Done about the same time as the bell-rope?" remarked Holmes. + +"Yes, there were several little changes carried out about that +time." + +"They seem to have been of a most interesting character--dummy +bell-ropes, and ventilators which do not ventilate. With your +permission, Miss Stoner, we shall now carry our researches into +the inner apartment." + +Dr. Grimesby Roylott's chamber was larger than that of his +step-daughter, but was as plainly furnished. A camp-bed, a small +wooden shelf full of books, mostly of a technical character, an +armchair beside the bed, a plain wooden chair against the wall, a +round table, and a large iron safe were the principal things +which met the eye. Holmes walked slowly round and examined each +and all of them with the keenest interest. + +"What's in here?" he asked, tapping the safe. + +"My stepfather's business papers." + +"Oh! you have seen inside, then?" + +"Only once, some years ago. I remember that it was full of +papers." + +"There isn't a cat in it, for example?" + +"No. What a strange idea!" + +"Well, look at this!" He took up a small saucer of milk which +stood on the top of it. + +"No; we don't keep a cat. But there is a cheetah and a baboon." + +"Ah, yes, of course! Well, a cheetah is just a big cat, and yet a +saucer of milk does not go very far in satisfying its wants, I +daresay. There is one point which I should wish to determine." He +squatted down in front of the wooden chair and examined the seat +of it with the greatest attention. + +"Thank you. That is quite settled," said he, rising and putting +his lens in his pocket. "Hullo! Here is something interesting!" + +The object which had caught his eye was a small dog lash hung on +one corner of the bed. The lash, however, was curled upon itself +and tied so as to make a loop of whipcord. + +"What do you make of that, Watson?" + +"It's a common enough lash. But I don't know why it should be +tied." + +"That is not quite so common, is it? Ah, me! it's a wicked world, +and when a clever man turns his brains to crime it is the worst +of all. I think that I have seen enough now, Miss Stoner, and +with your permission we shall walk out upon the lawn." + +I had never seen my friend's face so grim or his brow so dark as +it was when we turned from the scene of this investigation. We +had walked several times up and down the lawn, neither Miss +Stoner nor myself liking to break in upon his thoughts before he +roused himself from his reverie. + +"It is very essential, Miss Stoner," said he, "that you should +absolutely follow my advice in every respect." + +"I shall most certainly do so." + +"The matter is too serious for any hesitation. Your life may +depend upon your compliance." + +"I assure you that I am in your hands." + +"In the first place, both my friend and I must spend the night in +your room." + +Both Miss Stoner and I gazed at him in astonishment. + +"Yes, it must be so. Let me explain. I believe that that is the +village inn over there?" + +"Yes, that is the Crown." + +"Very good. Your windows would be visible from there?" + +"Certainly." + +"You must confine yourself to your room, on pretence of a +headache, when your stepfather comes back. Then when you hear him +retire for the night, you must open the shutters of your window, +undo the hasp, put your lamp there as a signal to us, and then +withdraw quietly with everything which you are likely to want +into the room which you used to occupy. I have no doubt that, in +spite of the repairs, you could manage there for one night." + +"Oh, yes, easily." + +"The rest you will leave in our hands." + +"But what will you do?" + +"We shall spend the night in your room, and we shall investigate +the cause of this noise which has disturbed you." + +"I believe, Mr. Holmes, that you have already made up your mind," +said Miss Stoner, laying her hand upon my companion's sleeve. + +"Perhaps I have." + +"Then, for pity's sake, tell me what was the cause of my sister's +death." + +"I should prefer to have clearer proofs before I speak." + +"You can at least tell me whether my own thought is correct, and +if she died from some sudden fright." + +"No, I do not think so. I think that there was probably some more +tangible cause. And now, Miss Stoner, we must leave you for if +Dr. Roylott returned and saw us our journey would be in vain. +Good-bye, and be brave, for if you will do what I have told you, +you may rest assured that we shall soon drive away the dangers +that threaten you." + +Sherlock Holmes and I had no difficulty in engaging a bedroom and +sitting-room at the Crown Inn. They were on the upper floor, and +from our window we could command a view of the avenue gate, and +of the inhabited wing of Stoke Moran Manor House. At dusk we saw +Dr. Grimesby Roylott drive past, his huge form looming up beside +the little figure of the lad who drove him. The boy had some +slight difficulty in undoing the heavy iron gates, and we heard +the hoarse roar of the doctor's voice and saw the fury with which +he shook his clinched fists at him. The trap drove on, and a few +minutes later we saw a sudden light spring up among the trees as +the lamp was lit in one of the sitting-rooms. + +"Do you know, Watson," said Holmes as we sat together in the +gathering darkness, "I have really some scruples as to taking you +to-night. There is a distinct element of danger." + +"Can I be of assistance?" + +"Your presence might be invaluable." + +"Then I shall certainly come." + +"It is very kind of you." + +"You speak of danger. You have evidently seen more in these rooms +than was visible to me." + +"No, but I fancy that I may have deduced a little more. I imagine +that you saw all that I did." + +"I saw nothing remarkable save the bell-rope, and what purpose +that could answer I confess is more than I can imagine." + +"You saw the ventilator, too?" + +"Yes, but I do not think that it is such a very unusual thing to +have a small opening between two rooms. It was so small that a +rat could hardly pass through." + +"I knew that we should find a ventilator before ever we came to +Stoke Moran." + +"My dear Holmes!" + +"Oh, yes, I did. You remember in her statement she said that her +sister could smell Dr. Roylott's cigar. Now, of course that +suggested at once that there must be a communication between the +two rooms. It could only be a small one, or it would have been +remarked upon at the coroner's inquiry. I deduced a ventilator." + +"But what harm can there be in that?" + +"Well, there is at least a curious coincidence of dates. A +ventilator is made, a cord is hung, and a lady who sleeps in the +bed dies. Does not that strike you?" + +"I cannot as yet see any connection." + +"Did you observe anything very peculiar about that bed?" + +"No." + +"It was clamped to the floor. Did you ever see a bed fastened +like that before?" + +"I cannot say that I have." + +"The lady could not move her bed. It must always be in the same +relative position to the ventilator and to the rope--or so we may +call it, since it was clearly never meant for a bell-pull." + +"Holmes," I cried, "I seem to see dimly what you are hinting at. +We are only just in time to prevent some subtle and horrible +crime." + +"Subtle enough and horrible enough. When a doctor does go wrong +he is the first of criminals. He has nerve and he has knowledge. +Palmer and Pritchard were among the heads of their profession. +This man strikes even deeper, but I think, Watson, that we shall +be able to strike deeper still. But we shall have horrors enough +before the night is over; for goodness' sake let us have a quiet +pipe and turn our minds for a few hours to something more +cheerful." + + +About nine o'clock the light among the trees was extinguished, +and all was dark in the direction of the Manor House. Two hours +passed slowly away, and then, suddenly, just at the stroke of +eleven, a single bright light shone out right in front of us. + +"That is our signal," said Holmes, springing to his feet; "it +comes from the middle window." + +As we passed out he exchanged a few words with the landlord, +explaining that we were going on a late visit to an acquaintance, +and that it was possible that we might spend the night there. A +moment later we were out on the dark road, a chill wind blowing +in our faces, and one yellow light twinkling in front of us +through the gloom to guide us on our sombre errand. + +There was little difficulty in entering the grounds, for +unrepaired breaches gaped in the old park wall. Making our way +among the trees, we reached the lawn, crossed it, and were about +to enter through the window when out from a clump of laurel +bushes there darted what seemed to be a hideous and distorted +child, who threw itself upon the grass with writhing limbs and +then ran swiftly across the lawn into the darkness. + +"My God!" I whispered; "did you see it?" + +Holmes was for the moment as startled as I. His hand closed like +a vice upon my wrist in his agitation. Then he broke into a low +laugh and put his lips to my ear. + +"It is a nice household," he murmured. "That is the baboon." + +I had forgotten the strange pets which the doctor affected. There +was a cheetah, too; perhaps we might find it upon our shoulders +at any moment. I confess that I felt easier in my mind when, +after following Holmes' example and slipping off my shoes, I +found myself inside the bedroom. My companion noiselessly closed +the shutters, moved the lamp onto the table, and cast his eyes +round the room. All was as we had seen it in the daytime. Then +creeping up to me and making a trumpet of his hand, he whispered +into my ear again so gently that it was all that I could do to +distinguish the words: + +"The least sound would be fatal to our plans." + +I nodded to show that I had heard. + +"We must sit without light. He would see it through the +ventilator." + +I nodded again. + +"Do not go asleep; your very life may depend upon it. Have your +pistol ready in case we should need it. I will sit on the side of +the bed, and you in that chair." + +I took out my revolver and laid it on the corner of the table. + +Holmes had brought up a long thin cane, and this he placed upon +the bed beside him. By it he laid the box of matches and the +stump of a candle. Then he turned down the lamp, and we were left +in darkness. + +How shall I ever forget that dreadful vigil? I could not hear a +sound, not even the drawing of a breath, and yet I knew that my +companion sat open-eyed, within a few feet of me, in the same +state of nervous tension in which I was myself. The shutters cut +off the least ray of light, and we waited in absolute darkness. + +From outside came the occasional cry of a night-bird, and once at +our very window a long drawn catlike whine, which told us that +the cheetah was indeed at liberty. Far away we could hear the +deep tones of the parish clock, which boomed out every quarter of +an hour. How long they seemed, those quarters! Twelve struck, and +one and two and three, and still we sat waiting silently for +whatever might befall. + +Suddenly there was the momentary gleam of a light up in the +direction of the ventilator, which vanished immediately, but was +succeeded by a strong smell of burning oil and heated metal. +Someone in the next room had lit a dark-lantern. I heard a gentle +sound of movement, and then all was silent once more, though the +smell grew stronger. For half an hour I sat with straining ears. +Then suddenly another sound became audible--a very gentle, +soothing sound, like that of a small jet of steam escaping +continually from a kettle. The instant that we heard it, Holmes +sprang from the bed, struck a match, and lashed furiously with +his cane at the bell-pull. + +"You see it, Watson?" he yelled. "You see it?" + +But I saw nothing. At the moment when Holmes struck the light I +heard a low, clear whistle, but the sudden glare flashing into my +weary eyes made it impossible for me to tell what it was at which +my friend lashed so savagely. I could, however, see that his face +was deadly pale and filled with horror and loathing. He had +ceased to strike and was gazing up at the ventilator when +suddenly there broke from the silence of the night the most +horrible cry to which I have ever listened. It swelled up louder +and louder, a hoarse yell of pain and fear and anger all mingled +in the one dreadful shriek. They say that away down in the +village, and even in the distant parsonage, that cry raised the +sleepers from their beds. It struck cold to our hearts, and I +stood gazing at Holmes, and he at me, until the last echoes of it +had died away into the silence from which it rose. + +"What can it mean?" I gasped. + +"It means that it is all over," Holmes answered. "And perhaps, +after all, it is for the best. Take your pistol, and we will +enter Dr. Roylott's room." + +With a grave face he lit the lamp and led the way down the +corridor. Twice he struck at the chamber door without any reply +from within. Then he turned the handle and entered, I at his +heels, with the cocked pistol in my hand. + +It was a singular sight which met our eyes. On the table stood a +dark-lantern with the shutter half open, throwing a brilliant +beam of light upon the iron safe, the door of which was ajar. +Beside this table, on the wooden chair, sat Dr. Grimesby Roylott +clad in a long grey dressing-gown, his bare ankles protruding +beneath, and his feet thrust into red heelless Turkish slippers. +Across his lap lay the short stock with the long lash which we +had noticed during the day. His chin was cocked upward and his +eyes were fixed in a dreadful, rigid stare at the corner of the +ceiling. Round his brow he had a peculiar yellow band, with +brownish speckles, which seemed to be bound tightly round his +head. As we entered he made neither sound nor motion. + +"The band! the speckled band!" whispered Holmes. + +I took a step forward. In an instant his strange headgear began +to move, and there reared itself from among his hair the squat +diamond-shaped head and puffed neck of a loathsome serpent. + +"It is a swamp adder!" cried Holmes; "the deadliest snake in +India. He has died within ten seconds of being bitten. Violence +does, in truth, recoil upon the violent, and the schemer falls +into the pit which he digs for another. Let us thrust this +creature back into its den, and we can then remove Miss Stoner to +some place of shelter and let the county police know what has +happened." + +As he spoke he drew the dog-whip swiftly from the dead man's lap, +and throwing the noose round the reptile's neck he drew it from +its horrid perch and, carrying it at arm's length, threw it into +the iron safe, which he closed upon it. + +Such are the true facts of the death of Dr. Grimesby Roylott, of +Stoke Moran. It is not necessary that I should prolong a +narrative which has already run to too great a length by telling +how we broke the sad news to the terrified girl, how we conveyed +her by the morning train to the care of her good aunt at Harrow, +of how the slow process of official inquiry came to the +conclusion that the doctor met his fate while indiscreetly +playing with a dangerous pet. The little which I had yet to learn +of the case was told me by Sherlock Holmes as we travelled back +next day. + +"I had," said he, "come to an entirely erroneous conclusion which +shows, my dear Watson, how dangerous it always is to reason from +insufficient data. The presence of the gipsies, and the use of +the word 'band,' which was used by the poor girl, no doubt, to +explain the appearance which she had caught a hurried glimpse of +by the light of her match, were sufficient to put me upon an +entirely wrong scent. I can only claim the merit that I instantly +reconsidered my position when, however, it became clear to me +that whatever danger threatened an occupant of the room could not +come either from the window or the door. My attention was +speedily drawn, as I have already remarked to you, to this +ventilator, and to the bell-rope which hung down to the bed. The +discovery that this was a dummy, and that the bed was clamped to +the floor, instantly gave rise to the suspicion that the rope was +there as a bridge for something passing through the hole and +coming to the bed. The idea of a snake instantly occurred to me, +and when I coupled it with my knowledge that the doctor was +furnished with a supply of creatures from India, I felt that I +was probably on the right track. The idea of using a form of +poison which could not possibly be discovered by any chemical +test was just such a one as would occur to a clever and ruthless +man who had had an Eastern training. The rapidity with which such +a poison would take effect would also, from his point of view, be +an advantage. It would be a sharp-eyed coroner, indeed, who could +distinguish the two little dark punctures which would show where +the poison fangs had done their work. Then I thought of the +whistle. Of course he must recall the snake before the morning +light revealed it to the victim. He had trained it, probably by +the use of the milk which we saw, to return to him when summoned. +He would put it through this ventilator at the hour that he +thought best, with the certainty that it would crawl down the +rope and land on the bed. It might or might not bite the +occupant, perhaps she might escape every night for a week, but +sooner or later she must fall a victim. + +"I had come to these conclusions before ever I had entered his +room. An inspection of his chair showed me that he had been in +the habit of standing on it, which of course would be necessary +in order that he should reach the ventilator. The sight of the +safe, the saucer of milk, and the loop of whipcord were enough to +finally dispel any doubts which may have remained. The metallic +clang heard by Miss Stoner was obviously caused by her stepfather +hastily closing the door of his safe upon its terrible occupant. +Having once made up my mind, you know the steps which I took in +order to put the matter to the proof. I heard the creature hiss +as I have no doubt that you did also, and I instantly lit the +light and attacked it." + +"With the result of driving it through the ventilator." + +"And also with the result of causing it to turn upon its master +at the other side. Some of the blows of my cane came home and +roused its snakish temper, so that it flew upon the first person +it saw. In this way I am no doubt indirectly responsible for Dr. +Grimesby Roylott's death, and I cannot say that it is likely to +weigh very heavily upon my conscience." + + + +IX. THE ADVENTURE OF THE ENGINEER'S THUMB + +Of all the problems which have been submitted to my friend, Mr. +Sherlock Holmes, for solution during the years of our intimacy, +there were only two which I was the means of introducing to his +notice--that of Mr. Hatherley's thumb, and that of Colonel +Warburton's madness. Of these the latter may have afforded a +finer field for an acute and original observer, but the other was +so strange in its inception and so dramatic in its details that +it may be the more worthy of being placed upon record, even if it +gave my friend fewer openings for those deductive methods of +reasoning by which he achieved such remarkable results. The story +has, I believe, been told more than once in the newspapers, but, +like all such narratives, its effect is much less striking when +set forth en bloc in a single half-column of print than when the +facts slowly evolve before your own eyes, and the mystery clears +gradually away as each new discovery furnishes a step which leads +on to the complete truth. At the time the circumstances made a +deep impression upon me, and the lapse of two years has hardly +served to weaken the effect. + +It was in the summer of '89, not long after my marriage, that the +events occurred which I am now about to summarise. I had returned +to civil practice and had finally abandoned Holmes in his Baker +Street rooms, although I continually visited him and occasionally +even persuaded him to forgo his Bohemian habits so far as to come +and visit us. My practice had steadily increased, and as I +happened to live at no very great distance from Paddington +Station, I got a few patients from among the officials. One of +these, whom I had cured of a painful and lingering disease, was +never weary of advertising my virtues and of endeavouring to send +me on every sufferer over whom he might have any influence. + +One morning, at a little before seven o'clock, I was awakened by +the maid tapping at the door to announce that two men had come +from Paddington and were waiting in the consulting-room. I +dressed hurriedly, for I knew by experience that railway cases +were seldom trivial, and hastened downstairs. As I descended, my +old ally, the guard, came out of the room and closed the door +tightly behind him. + +"I've got him here," he whispered, jerking his thumb over his +shoulder; "he's all right." + +"What is it, then?" I asked, for his manner suggested that it was +some strange creature which he had caged up in my room. + +"It's a new patient," he whispered. "I thought I'd bring him +round myself; then he couldn't slip away. There he is, all safe +and sound. I must go now, Doctor; I have my dooties, just the +same as you." And off he went, this trusty tout, without even +giving me time to thank him. + +I entered my consulting-room and found a gentleman seated by the +table. He was quietly dressed in a suit of heather tweed with a +soft cloth cap which he had laid down upon my books. Round one of +his hands he had a handkerchief wrapped, which was mottled all +over with bloodstains. He was young, not more than +five-and-twenty, I should say, with a strong, masculine face; but +he was exceedingly pale and gave me the impression of a man who +was suffering from some strong agitation, which it took all his +strength of mind to control. + +"I am sorry to knock you up so early, Doctor," said he, "but I +have had a very serious accident during the night. I came in by +train this morning, and on inquiring at Paddington as to where I +might find a doctor, a worthy fellow very kindly escorted me +here. I gave the maid a card, but I see that she has left it upon +the side-table." + +I took it up and glanced at it. "Mr. Victor Hatherley, hydraulic +engineer, 16A, Victoria Street (3rd floor)." That was the name, +style, and abode of my morning visitor. "I regret that I have +kept you waiting," said I, sitting down in my library-chair. "You +are fresh from a night journey, I understand, which is in itself +a monotonous occupation." + +"Oh, my night could not be called monotonous," said he, and +laughed. He laughed very heartily, with a high, ringing note, +leaning back in his chair and shaking his sides. All my medical +instincts rose up against that laugh. + +"Stop it!" I cried; "pull yourself together!" and I poured out +some water from a caraffe. + +It was useless, however. He was off in one of those hysterical +outbursts which come upon a strong nature when some great crisis +is over and gone. Presently he came to himself once more, very +weary and pale-looking. + +"I have been making a fool of myself," he gasped. + +"Not at all. Drink this." I dashed some brandy into the water, +and the colour began to come back to his bloodless cheeks. + +"That's better!" said he. "And now, Doctor, perhaps you would +kindly attend to my thumb, or rather to the place where my thumb +used to be." + +He unwound the handkerchief and held out his hand. It gave even +my hardened nerves a shudder to look at it. There were four +protruding fingers and a horrid red, spongy surface where the +thumb should have been. It had been hacked or torn right out from +the roots. + +"Good heavens!" I cried, "this is a terrible injury. It must have +bled considerably." + +"Yes, it did. I fainted when it was done, and I think that I must +have been senseless for a long time. When I came to I found that +it was still bleeding, so I tied one end of my handkerchief very +tightly round the wrist and braced it up with a twig." + +"Excellent! You should have been a surgeon." + +"It is a question of hydraulics, you see, and came within my own +province." + +"This has been done," said I, examining the wound, "by a very +heavy and sharp instrument." + +"A thing like a cleaver," said he. + +"An accident, I presume?" + +"By no means." + +"What! a murderous attack?" + +"Very murderous indeed." + +"You horrify me." + +I sponged the wound, cleaned it, dressed it, and finally covered +it over with cotton wadding and carbolised bandages. He lay back +without wincing, though he bit his lip from time to time. + +"How is that?" I asked when I had finished. + +"Capital! Between your brandy and your bandage, I feel a new man. +I was very weak, but I have had a good deal to go through." + +"Perhaps you had better not speak of the matter. It is evidently +trying to your nerves." + +"Oh, no, not now. I shall have to tell my tale to the police; +but, between ourselves, if it were not for the convincing +evidence of this wound of mine, I should be surprised if they +believed my statement, for it is a very extraordinary one, and I +have not much in the way of proof with which to back it up; and, +even if they believe me, the clues which I can give them are so +vague that it is a question whether justice will be done." + +"Ha!" cried I, "if it is anything in the nature of a problem +which you desire to see solved, I should strongly recommend you +to come to my friend, Mr. Sherlock Holmes, before you go to the +official police." + +"Oh, I have heard of that fellow," answered my visitor, "and I +should be very glad if he would take the matter up, though of +course I must use the official police as well. Would you give me +an introduction to him?" + +"I'll do better. I'll take you round to him myself." + +"I should be immensely obliged to you." + +"We'll call a cab and go together. We shall just be in time to +have a little breakfast with him. Do you feel equal to it?" + +"Yes; I shall not feel easy until I have told my story." + +"Then my servant will call a cab, and I shall be with you in an +instant." I rushed upstairs, explained the matter shortly to my +wife, and in five minutes was inside a hansom, driving with my +new acquaintance to Baker Street. + +Sherlock Holmes was, as I expected, lounging about his +sitting-room in his dressing-gown, reading the agony column of The +Times and smoking his before-breakfast pipe, which was composed +of all the plugs and dottles left from his smokes of the day +before, all carefully dried and collected on the corner of the +mantelpiece. He received us in his quietly genial fashion, +ordered fresh rashers and eggs, and joined us in a hearty meal. +When it was concluded he settled our new acquaintance upon the +sofa, placed a pillow beneath his head, and laid a glass of +brandy and water within his reach. + +"It is easy to see that your experience has been no common one, +Mr. Hatherley," said he. "Pray, lie down there and make yourself +absolutely at home. Tell us what you can, but stop when you are +tired and keep up your strength with a little stimulant." + +"Thank you," said my patient, "but I have felt another man since +the doctor bandaged me, and I think that your breakfast has +completed the cure. I shall take up as little of your valuable +time as possible, so I shall start at once upon my peculiar +experiences." + +Holmes sat in his big armchair with the weary, heavy-lidded +expression which veiled his keen and eager nature, while I sat +opposite to him, and we listened in silence to the strange story +which our visitor detailed to us. + +"You must know," said he, "that I am an orphan and a bachelor, +residing alone in lodgings in London. By profession I am a +hydraulic engineer, and I have had considerable experience of my +work during the seven years that I was apprenticed to Venner & +Matheson, the well-known firm, of Greenwich. Two years ago, +having served my time, and having also come into a fair sum of +money through my poor father's death, I determined to start in +business for myself and took professional chambers in Victoria +Street. + +"I suppose that everyone finds his first independent start in +business a dreary experience. To me it has been exceptionally so. +During two years I have had three consultations and one small +job, and that is absolutely all that my profession has brought +me. My gross takings amount to 27 pounds 10s. Every day, from +nine in the morning until four in the afternoon, I waited in my +little den, until at last my heart began to sink, and I came to +believe that I should never have any practice at all. + +"Yesterday, however, just as I was thinking of leaving the +office, my clerk entered to say there was a gentleman waiting who +wished to see me upon business. He brought up a card, too, with +the name of 'Colonel Lysander Stark' engraved upon it. Close at +his heels came the colonel himself, a man rather over the middle +size, but of an exceeding thinness. I do not think that I have +ever seen so thin a man. His whole face sharpened away into nose +and chin, and the skin of his cheeks was drawn quite tense over +his outstanding bones. Yet this emaciation seemed to be his +natural habit, and due to no disease, for his eye was bright, his +step brisk, and his bearing assured. He was plainly but neatly +dressed, and his age, I should judge, would be nearer forty than +thirty. + +"'Mr. Hatherley?' said he, with something of a German accent. +'You have been recommended to me, Mr. Hatherley, as being a man +who is not only proficient in his profession but is also discreet +and capable of preserving a secret.' + +"I bowed, feeling as flattered as any young man would at such an +address. 'May I ask who it was who gave me so good a character?' + +"'Well, perhaps it is better that I should not tell you that just +at this moment. I have it from the same source that you are both +an orphan and a bachelor and are residing alone in London.' + +"'That is quite correct,' I answered; 'but you will excuse me if +I say that I cannot see how all this bears upon my professional +qualifications. I understand that it was on a professional matter +that you wished to speak to me?' + +"'Undoubtedly so. But you will find that all I say is really to +the point. I have a professional commission for you, but absolute +secrecy is quite essential--absolute secrecy, you understand, and +of course we may expect that more from a man who is alone than +from one who lives in the bosom of his family.' + +"'If I promise to keep a secret,' said I, 'you may absolutely +depend upon my doing so.' + +"He looked very hard at me as I spoke, and it seemed to me that I +had never seen so suspicious and questioning an eye. + +"'Do you promise, then?' said he at last. + +"'Yes, I promise.' + +"'Absolute and complete silence before, during, and after? No +reference to the matter at all, either in word or writing?' + +"'I have already given you my word.' + +"'Very good.' He suddenly sprang up, and darting like lightning +across the room he flung open the door. The passage outside was +empty. + +"'That's all right,' said he, coming back. 'I know that clerks are +sometimes curious as to their master's affairs. Now we can talk +in safety.' He drew up his chair very close to mine and began to +stare at me again with the same questioning and thoughtful look. + +"A feeling of repulsion, and of something akin to fear had begun +to rise within me at the strange antics of this fleshless man. +Even my dread of losing a client could not restrain me from +showing my impatience. + +"'I beg that you will state your business, sir,' said I; 'my time +is of value.' Heaven forgive me for that last sentence, but the +words came to my lips. + +"'How would fifty guineas for a night's work suit you?' he asked. + +"'Most admirably.' + +"'I say a night's work, but an hour's would be nearer the mark. I +simply want your opinion about a hydraulic stamping machine which +has got out of gear. If you show us what is wrong we shall soon +set it right ourselves. What do you think of such a commission as +that?' + +"'The work appears to be light and the pay munificent.' + +"'Precisely so. We shall want you to come to-night by the last +train.' + +"'Where to?' + +"'To Eyford, in Berkshire. It is a little place near the borders +of Oxfordshire, and within seven miles of Reading. There is a +train from Paddington which would bring you there at about +11:15.' + +"'Very good.' + +"'I shall come down in a carriage to meet you.' + +"'There is a drive, then?' + +"'Yes, our little place is quite out in the country. It is a good +seven miles from Eyford Station.' + +"'Then we can hardly get there before midnight. I suppose there +would be no chance of a train back. I should be compelled to stop +the night.' + +"'Yes, we could easily give you a shake-down.' + +"'That is very awkward. Could I not come at some more convenient +hour?' + +"'We have judged it best that you should come late. It is to +recompense you for any inconvenience that we are paying to you, a +young and unknown man, a fee which would buy an opinion from the +very heads of your profession. Still, of course, if you would +like to draw out of the business, there is plenty of time to do +so.' + +"I thought of the fifty guineas, and of how very useful they +would be to me. 'Not at all,' said I, 'I shall be very happy to +accommodate myself to your wishes. I should like, however, to +understand a little more clearly what it is that you wish me to +do.' + +"'Quite so. It is very natural that the pledge of secrecy which +we have exacted from you should have aroused your curiosity. I +have no wish to commit you to anything without your having it all +laid before you. I suppose that we are absolutely safe from +eavesdroppers?' + +"'Entirely.' + +"'Then the matter stands thus. You are probably aware that +fuller's-earth is a valuable product, and that it is only found +in one or two places in England?' + +"'I have heard so.' + +"'Some little time ago I bought a small place--a very small +place--within ten miles of Reading. I was fortunate enough to +discover that there was a deposit of fuller's-earth in one of my +fields. On examining it, however, I found that this deposit was a +comparatively small one, and that it formed a link between two +very much larger ones upon the right and left--both of them, +however, in the grounds of my neighbours. These good people were +absolutely ignorant that their land contained that which was +quite as valuable as a gold-mine. Naturally, it was to my +interest to buy their land before they discovered its true value, +but unfortunately I had no capital by which I could do this. I +took a few of my friends into the secret, however, and they +suggested that we should quietly and secretly work our own little +deposit and that in this way we should earn the money which would +enable us to buy the neighbouring fields. This we have now been +doing for some time, and in order to help us in our operations we +erected a hydraulic press. This press, as I have already +explained, has got out of order, and we wish your advice upon the +subject. We guard our secret very jealously, however, and if it +once became known that we had hydraulic engineers coming to our +little house, it would soon rouse inquiry, and then, if the facts +came out, it would be good-bye to any chance of getting these +fields and carrying out our plans. That is why I have made you +promise me that you will not tell a human being that you are +going to Eyford to-night. I hope that I make it all plain?' + +"'I quite follow you,' said I. 'The only point which I could not +quite understand was what use you could make of a hydraulic press +in excavating fuller's-earth, which, as I understand, is dug out +like gravel from a pit.' + +"'Ah!' said he carelessly, 'we have our own process. We compress +the earth into bricks, so as to remove them without revealing +what they are. But that is a mere detail. I have taken you fully +into my confidence now, Mr. Hatherley, and I have shown you how I +trust you.' He rose as he spoke. 'I shall expect you, then, at +Eyford at 11:15.' + +"'I shall certainly be there.' + +"'And not a word to a soul.' He looked at me with a last long, +questioning gaze, and then, pressing my hand in a cold, dank +grasp, he hurried from the room. + +"Well, when I came to think it all over in cool blood I was very +much astonished, as you may both think, at this sudden commission +which had been intrusted to me. On the one hand, of course, I was +glad, for the fee was at least tenfold what I should have asked +had I set a price upon my own services, and it was possible that +this order might lead to other ones. On the other hand, the face +and manner of my patron had made an unpleasant impression upon +me, and I could not think that his explanation of the +fuller's-earth was sufficient to explain the necessity for my +coming at midnight, and his extreme anxiety lest I should tell +anyone of my errand. However, I threw all fears to the winds, ate +a hearty supper, drove to Paddington, and started off, having +obeyed to the letter the injunction as to holding my tongue. + +"At Reading I had to change not only my carriage but my station. +However, I was in time for the last train to Eyford, and I +reached the little dim-lit station after eleven o'clock. I was the +only passenger who got out there, and there was no one upon the +platform save a single sleepy porter with a lantern. As I passed +out through the wicket gate, however, I found my acquaintance of +the morning waiting in the shadow upon the other side. Without a +word he grasped my arm and hurried me into a carriage, the door +of which was standing open. He drew up the windows on either +side, tapped on the wood-work, and away we went as fast as the +horse could go." + +"One horse?" interjected Holmes. + +"Yes, only one." + +"Did you observe the colour?" + +"Yes, I saw it by the side-lights when I was stepping into the +carriage. It was a chestnut." + +"Tired-looking or fresh?" + +"Oh, fresh and glossy." + +"Thank you. I am sorry to have interrupted you. Pray continue +your most interesting statement." + +"Away we went then, and we drove for at least an hour. Colonel +Lysander Stark had said that it was only seven miles, but I +should think, from the rate that we seemed to go, and from the +time that we took, that it must have been nearer twelve. He sat +at my side in silence all the time, and I was aware, more than +once when I glanced in his direction, that he was looking at me +with great intensity. The country roads seem to be not very good +in that part of the world, for we lurched and jolted terribly. I +tried to look out of the windows to see something of where we +were, but they were made of frosted glass, and I could make out +nothing save the occasional bright blur of a passing light. Now +and then I hazarded some remark to break the monotony of the +journey, but the colonel answered only in monosyllables, and the +conversation soon flagged. At last, however, the bumping of the +road was exchanged for the crisp smoothness of a gravel-drive, +and the carriage came to a stand. Colonel Lysander Stark sprang +out, and, as I followed after him, pulled me swiftly into a porch +which gaped in front of us. We stepped, as it were, right out of +the carriage and into the hall, so that I failed to catch the +most fleeting glance of the front of the house. The instant that +I had crossed the threshold the door slammed heavily behind us, +and I heard faintly the rattle of the wheels as the carriage +drove away. + +"It was pitch dark inside the house, and the colonel fumbled +about looking for matches and muttering under his breath. +Suddenly a door opened at the other end of the passage, and a +long, golden bar of light shot out in our direction. It grew +broader, and a woman appeared with a lamp in her hand, which she +held above her head, pushing her face forward and peering at us. +I could see that she was pretty, and from the gloss with which +the light shone upon her dark dress I knew that it was a rich +material. She spoke a few words in a foreign tongue in a tone as +though asking a question, and when my companion answered in a +gruff monosyllable she gave such a start that the lamp nearly +fell from her hand. Colonel Stark went up to her, whispered +something in her ear, and then, pushing her back into the room +from whence she had come, he walked towards me again with the +lamp in his hand. + +"'Perhaps you will have the kindness to wait in this room for a +few minutes,' said he, throwing open another door. It was a +quiet, little, plainly furnished room, with a round table in the +centre, on which several German books were scattered. Colonel +Stark laid down the lamp on the top of a harmonium beside the +door. 'I shall not keep you waiting an instant,' said he, and +vanished into the darkness. + +"I glanced at the books upon the table, and in spite of my +ignorance of German I could see that two of them were treatises +on science, the others being volumes of poetry. Then I walked +across to the window, hoping that I might catch some glimpse of +the country-side, but an oak shutter, heavily barred, was folded +across it. It was a wonderfully silent house. There was an old +clock ticking loudly somewhere in the passage, but otherwise +everything was deadly still. A vague feeling of uneasiness began +to steal over me. Who were these German people, and what were +they doing living in this strange, out-of-the-way place? And +where was the place? I was ten miles or so from Eyford, that was +all I knew, but whether north, south, east, or west I had no +idea. For that matter, Reading, and possibly other large towns, +were within that radius, so the place might not be so secluded, +after all. Yet it was quite certain, from the absolute stillness, +that we were in the country. I paced up and down the room, +humming a tune under my breath to keep up my spirits and feeling +that I was thoroughly earning my fifty-guinea fee. + +"Suddenly, without any preliminary sound in the midst of the +utter stillness, the door of my room swung slowly open. The woman +was standing in the aperture, the darkness of the hall behind +her, the yellow light from my lamp beating upon her eager and +beautiful face. I could see at a glance that she was sick with +fear, and the sight sent a chill to my own heart. She held up one +shaking finger to warn me to be silent, and she shot a few +whispered words of broken English at me, her eyes glancing back, +like those of a frightened horse, into the gloom behind her. + +"'I would go,' said she, trying hard, as it seemed to me, to +speak calmly; 'I would go. I should not stay here. There is no +good for you to do.' + +"'But, madam,' said I, 'I have not yet done what I came for. I +cannot possibly leave until I have seen the machine.' + +"'It is not worth your while to wait,' she went on. 'You can pass +through the door; no one hinders.' And then, seeing that I smiled +and shook my head, she suddenly threw aside her constraint and +made a step forward, with her hands wrung together. 'For the love +of Heaven!' she whispered, 'get away from here before it is too +late!' + +"But I am somewhat headstrong by nature, and the more ready to +engage in an affair when there is some obstacle in the way. I +thought of my fifty-guinea fee, of my wearisome journey, and of +the unpleasant night which seemed to be before me. Was it all to +go for nothing? Why should I slink away without having carried +out my commission, and without the payment which was my due? This +woman might, for all I knew, be a monomaniac. With a stout +bearing, therefore, though her manner had shaken me more than I +cared to confess, I still shook my head and declared my intention +of remaining where I was. She was about to renew her entreaties +when a door slammed overhead, and the sound of several footsteps +was heard upon the stairs. She listened for an instant, threw up +her hands with a despairing gesture, and vanished as suddenly and +as noiselessly as she had come. + +"The newcomers were Colonel Lysander Stark and a short thick man +with a chinchilla beard growing out of the creases of his double +chin, who was introduced to me as Mr. Ferguson. + +"'This is my secretary and manager,' said the colonel. 'By the +way, I was under the impression that I left this door shut just +now. I fear that you have felt the draught.' + +"'On the contrary,' said I, 'I opened the door myself because I +felt the room to be a little close.' + +"He shot one of his suspicious looks at me. 'Perhaps we had +better proceed to business, then,' said he. 'Mr. Ferguson and I +will take you up to see the machine.' + +"'I had better put my hat on, I suppose.' + +"'Oh, no, it is in the house.' + +"'What, you dig fuller's-earth in the house?' + +"'No, no. This is only where we compress it. But never mind that. +All we wish you to do is to examine the machine and to let us +know what is wrong with it.' + +"We went upstairs together, the colonel first with the lamp, the +fat manager and I behind him. It was a labyrinth of an old house, +with corridors, passages, narrow winding staircases, and little +low doors, the thresholds of which were hollowed out by the +generations who had crossed them. There were no carpets and no +signs of any furniture above the ground floor, while the plaster +was peeling off the walls, and the damp was breaking through in +green, unhealthy blotches. I tried to put on as unconcerned an +air as possible, but I had not forgotten the warnings of the +lady, even though I disregarded them, and I kept a keen eye upon +my two companions. Ferguson appeared to be a morose and silent +man, but I could see from the little that he said that he was at +least a fellow-countryman. + +"Colonel Lysander Stark stopped at last before a low door, which +he unlocked. Within was a small, square room, in which the three +of us could hardly get at one time. Ferguson remained outside, +and the colonel ushered me in. + +"'We are now,' said he, 'actually within the hydraulic press, and +it would be a particularly unpleasant thing for us if anyone were +to turn it on. The ceiling of this small chamber is really the +end of the descending piston, and it comes down with the force of +many tons upon this metal floor. There are small lateral columns +of water outside which receive the force, and which transmit and +multiply it in the manner which is familiar to you. The machine +goes readily enough, but there is some stiffness in the working +of it, and it has lost a little of its force. Perhaps you will +have the goodness to look it over and to show us how we can set +it right.' + +"I took the lamp from him, and I examined the machine very +thoroughly. It was indeed a gigantic one, and capable of +exercising enormous pressure. When I passed outside, however, and +pressed down the levers which controlled it, I knew at once by +the whishing sound that there was a slight leakage, which allowed +a regurgitation of water through one of the side cylinders. An +examination showed that one of the india-rubber bands which was +round the head of a driving-rod had shrunk so as not quite to +fill the socket along which it worked. This was clearly the cause +of the loss of power, and I pointed it out to my companions, who +followed my remarks very carefully and asked several practical +questions as to how they should proceed to set it right. When I +had made it clear to them, I returned to the main chamber of the +machine and took a good look at it to satisfy my own curiosity. +It was obvious at a glance that the story of the fuller's-earth +was the merest fabrication, for it would be absurd to suppose +that so powerful an engine could be designed for so inadequate a +purpose. The walls were of wood, but the floor consisted of a +large iron trough, and when I came to examine it I could see a +crust of metallic deposit all over it. I had stooped and was +scraping at this to see exactly what it was when I heard a +muttered exclamation in German and saw the cadaverous face of the +colonel looking down at me. + +"'What are you doing there?' he asked. + +"I felt angry at having been tricked by so elaborate a story as +that which he had told me. 'I was admiring your fuller's-earth,' +said I; 'I think that I should be better able to advise you as to +your machine if I knew what the exact purpose was for which it +was used.' + +"The instant that I uttered the words I regretted the rashness of +my speech. His face set hard, and a baleful light sprang up in +his grey eyes. + +"'Very well,' said he, 'you shall know all about the machine.' He +took a step backward, slammed the little door, and turned the key +in the lock. I rushed towards it and pulled at the handle, but it +was quite secure, and did not give in the least to my kicks and +shoves. 'Hullo!' I yelled. 'Hullo! Colonel! Let me out!' + +"And then suddenly in the silence I heard a sound which sent my +heart into my mouth. It was the clank of the levers and the swish +of the leaking cylinder. He had set the engine at work. The lamp +still stood upon the floor where I had placed it when examining +the trough. By its light I saw that the black ceiling was coming +down upon me, slowly, jerkily, but, as none knew better than +myself, with a force which must within a minute grind me to a +shapeless pulp. I threw myself, screaming, against the door, and +dragged with my nails at the lock. I implored the colonel to let +me out, but the remorseless clanking of the levers drowned my +cries. The ceiling was only a foot or two above my head, and with +my hand upraised I could feel its hard, rough surface. Then it +flashed through my mind that the pain of my death would depend +very much upon the position in which I met it. If I lay on my +face the weight would come upon my spine, and I shuddered to +think of that dreadful snap. Easier the other way, perhaps; and +yet, had I the nerve to lie and look up at that deadly black +shadow wavering down upon me? Already I was unable to stand +erect, when my eye caught something which brought a gush of hope +back to my heart. + +"I have said that though the floor and ceiling were of iron, the +walls were of wood. As I gave a last hurried glance around, I saw +a thin line of yellow light between two of the boards, which +broadened and broadened as a small panel was pushed backward. For +an instant I could hardly believe that here was indeed a door +which led away from death. The next instant I threw myself +through, and lay half-fainting upon the other side. The panel had +closed again behind me, but the crash of the lamp, and a few +moments afterwards the clang of the two slabs of metal, told me +how narrow had been my escape. + +"I was recalled to myself by a frantic plucking at my wrist, and +I found myself lying upon the stone floor of a narrow corridor, +while a woman bent over me and tugged at me with her left hand, +while she held a candle in her right. It was the same good friend +whose warning I had so foolishly rejected. + +"'Come! come!' she cried breathlessly. 'They will be here in a +moment. They will see that you are not there. Oh, do not waste +the so-precious time, but come!' + +"This time, at least, I did not scorn her advice. I staggered to +my feet and ran with her along the corridor and down a winding +stair. The latter led to another broad passage, and just as we +reached it we heard the sound of running feet and the shouting of +two voices, one answering the other from the floor on which we +were and from the one beneath. My guide stopped and looked about +her like one who is at her wit's end. Then she threw open a door +which led into a bedroom, through the window of which the moon +was shining brightly. + +"'It is your only chance,' said she. 'It is high, but it may be +that you can jump it.' + +"As she spoke a light sprang into view at the further end of the +passage, and I saw the lean figure of Colonel Lysander Stark +rushing forward with a lantern in one hand and a weapon like a +butcher's cleaver in the other. I rushed across the bedroom, +flung open the window, and looked out. How quiet and sweet and +wholesome the garden looked in the moonlight, and it could not be +more than thirty feet down. I clambered out upon the sill, but I +hesitated to jump until I should have heard what passed between +my saviour and the ruffian who pursued me. If she were ill-used, +then at any risks I was determined to go back to her assistance. +The thought had hardly flashed through my mind before he was at +the door, pushing his way past her; but she threw her arms round +him and tried to hold him back. + +"'Fritz! Fritz!' she cried in English, 'remember your promise +after the last time. You said it should not be again. He will be +silent! Oh, he will be silent!' + +"'You are mad, Elise!' he shouted, struggling to break away from +her. 'You will be the ruin of us. He has seen too much. Let me +pass, I say!' He dashed her to one side, and, rushing to the +window, cut at me with his heavy weapon. I had let myself go, and +was hanging by the hands to the sill, when his blow fell. I was +conscious of a dull pain, my grip loosened, and I fell into the +garden below. + +"I was shaken but not hurt by the fall; so I picked myself up and +rushed off among the bushes as hard as I could run, for I +understood that I was far from being out of danger yet. Suddenly, +however, as I ran, a deadly dizziness and sickness came over me. +I glanced down at my hand, which was throbbing painfully, and +then, for the first time, saw that my thumb had been cut off and +that the blood was pouring from my wound. I endeavoured to tie my +handkerchief round it, but there came a sudden buzzing in my +ears, and next moment I fell in a dead faint among the +rose-bushes. + +"How long I remained unconscious I cannot tell. It must have been +a very long time, for the moon had sunk, and a bright morning was +breaking when I came to myself. My clothes were all sodden with +dew, and my coat-sleeve was drenched with blood from my wounded +thumb. The smarting of it recalled in an instant all the +particulars of my night's adventure, and I sprang to my feet with +the feeling that I might hardly yet be safe from my pursuers. But +to my astonishment, when I came to look round me, neither house +nor garden were to be seen. I had been lying in an angle of the +hedge close by the highroad, and just a little lower down was a +long building, which proved, upon my approaching it, to be the +very station at which I had arrived upon the previous night. Were +it not for the ugly wound upon my hand, all that had passed +during those dreadful hours might have been an evil dream. + +"Half dazed, I went into the station and asked about the morning +train. There would be one to Reading in less than an hour. The +same porter was on duty, I found, as had been there when I +arrived. I inquired of him whether he had ever heard of Colonel +Lysander Stark. The name was strange to him. Had he observed a +carriage the night before waiting for me? No, he had not. Was +there a police-station anywhere near? There was one about three +miles off. + +"It was too far for me to go, weak and ill as I was. I determined +to wait until I got back to town before telling my story to the +police. It was a little past six when I arrived, so I went first +to have my wound dressed, and then the doctor was kind enough to +bring me along here. I put the case into your hands and shall do +exactly what you advise." + +We both sat in silence for some little time after listening to +this extraordinary narrative. Then Sherlock Holmes pulled down +from the shelf one of the ponderous commonplace books in which he +placed his cuttings. + +"Here is an advertisement which will interest you," said he. "It +appeared in all the papers about a year ago. Listen to this: +'Lost, on the 9th inst., Mr. Jeremiah Hayling, aged +twenty-six, a hydraulic engineer. Left his lodgings at ten +o'clock at night, and has not been heard of since. Was +dressed in,' etc., etc. Ha! That represents the last time that +the colonel needed to have his machine overhauled, I fancy." + +"Good heavens!" cried my patient. "Then that explains what the +girl said." + +"Undoubtedly. It is quite clear that the colonel was a cool and +desperate man, who was absolutely determined that nothing should +stand in the way of his little game, like those out-and-out +pirates who will leave no survivor from a captured ship. Well, +every moment now is precious, so if you feel equal to it we shall +go down to Scotland Yard at once as a preliminary to starting for +Eyford." + +Some three hours or so afterwards we were all in the train +together, bound from Reading to the little Berkshire village. +There were Sherlock Holmes, the hydraulic engineer, Inspector +Bradstreet, of Scotland Yard, a plain-clothes man, and myself. +Bradstreet had spread an ordnance map of the county out upon the +seat and was busy with his compasses drawing a circle with Eyford +for its centre. + +"There you are," said he. "That circle is drawn at a radius of +ten miles from the village. The place we want must be somewhere +near that line. You said ten miles, I think, sir." + +"It was an hour's good drive." + +"And you think that they brought you back all that way when you +were unconscious?" + +"They must have done so. I have a confused memory, too, of having +been lifted and conveyed somewhere." + +"What I cannot understand," said I, "is why they should have +spared you when they found you lying fainting in the garden. +Perhaps the villain was softened by the woman's entreaties." + +"I hardly think that likely. I never saw a more inexorable face +in my life." + +"Oh, we shall soon clear up all that," said Bradstreet. "Well, I +have drawn my circle, and I only wish I knew at what point upon +it the folk that we are in search of are to be found." + +"I think I could lay my finger on it," said Holmes quietly. + +"Really, now!" cried the inspector, "you have formed your +opinion! Come, now, we shall see who agrees with you. I say it is +south, for the country is more deserted there." + +"And I say east," said my patient. + +"I am for west," remarked the plain-clothes man. "There are +several quiet little villages up there." + +"And I am for north," said I, "because there are no hills there, +and our friend says that he did not notice the carriage go up +any." + +"Come," cried the inspector, laughing; "it's a very pretty +diversity of opinion. We have boxed the compass among us. Who do +you give your casting vote to?" + +"You are all wrong." + +"But we can't all be." + +"Oh, yes, you can. This is my point." He placed his finger in the +centre of the circle. "This is where we shall find them." + +"But the twelve-mile drive?" gasped Hatherley. + +"Six out and six back. Nothing simpler. You say yourself that the +horse was fresh and glossy when you got in. How could it be that +if it had gone twelve miles over heavy roads?" + +"Indeed, it is a likely ruse enough," observed Bradstreet +thoughtfully. "Of course there can be no doubt as to the nature +of this gang." + +"None at all," said Holmes. "They are coiners on a large scale, +and have used the machine to form the amalgam which has taken the +place of silver." + +"We have known for some time that a clever gang was at work," +said the inspector. "They have been turning out half-crowns by +the thousand. We even traced them as far as Reading, but could +get no farther, for they had covered their traces in a way that +showed that they were very old hands. But now, thanks to this +lucky chance, I think that we have got them right enough." + +But the inspector was mistaken, for those criminals were not +destined to fall into the hands of justice. As we rolled into +Eyford Station we saw a gigantic column of smoke which streamed +up from behind a small clump of trees in the neighbourhood and +hung like an immense ostrich feather over the landscape. + +"A house on fire?" asked Bradstreet as the train steamed off +again on its way. + +"Yes, sir!" said the station-master. + +"When did it break out?" + +"I hear that it was during the night, sir, but it has got worse, +and the whole place is in a blaze." + +"Whose house is it?" + +"Dr. Becher's." + +"Tell me," broke in the engineer, "is Dr. Becher a German, very +thin, with a long, sharp nose?" + +The station-master laughed heartily. "No, sir, Dr. Becher is an +Englishman, and there isn't a man in the parish who has a +better-lined waistcoat. But he has a gentleman staying with him, +a patient, as I understand, who is a foreigner, and he looks as +if a little good Berkshire beef would do him no harm." + +The station-master had not finished his speech before we were all +hastening in the direction of the fire. The road topped a low +hill, and there was a great widespread whitewashed building in +front of us, spouting fire at every chink and window, while in +the garden in front three fire-engines were vainly striving to +keep the flames under. + +"That's it!" cried Hatherley, in intense excitement. "There is +the gravel-drive, and there are the rose-bushes where I lay. That +second window is the one that I jumped from." + +"Well, at least," said Holmes, "you have had your revenge upon +them. There can be no question that it was your oil-lamp which, +when it was crushed in the press, set fire to the wooden walls, +though no doubt they were too excited in the chase after you to +observe it at the time. Now keep your eyes open in this crowd for +your friends of last night, though I very much fear that they are +a good hundred miles off by now." + +And Holmes' fears came to be realised, for from that day to this +no word has ever been heard either of the beautiful woman, the +sinister German, or the morose Englishman. Early that morning a +peasant had met a cart containing several people and some very +bulky boxes driving rapidly in the direction of Reading, but +there all traces of the fugitives disappeared, and even Holmes' +ingenuity failed ever to discover the least clue as to their +whereabouts. + +The firemen had been much perturbed at the strange arrangements +which they had found within, and still more so by discovering a +newly severed human thumb upon a window-sill of the second floor. +About sunset, however, their efforts were at last successful, and +they subdued the flames, but not before the roof had fallen in, +and the whole place been reduced to such absolute ruin that, save +some twisted cylinders and iron piping, not a trace remained of +the machinery which had cost our unfortunate acquaintance so +dearly. Large masses of nickel and of tin were discovered stored +in an out-house, but no coins were to be found, which may have +explained the presence of those bulky boxes which have been +already referred to. + +How our hydraulic engineer had been conveyed from the garden to +the spot where he recovered his senses might have remained +forever a mystery were it not for the soft mould, which told us a +very plain tale. He had evidently been carried down by two +persons, one of whom had remarkably small feet and the other +unusually large ones. On the whole, it was most probable that the +silent Englishman, being less bold or less murderous than his +companion, had assisted the woman to bear the unconscious man out +of the way of danger. + +"Well," said our engineer ruefully as we took our seats to return +once more to London, "it has been a pretty business for me! I +have lost my thumb and I have lost a fifty-guinea fee, and what +have I gained?" + +"Experience," said Holmes, laughing. "Indirectly it may be of +value, you know; you have only to put it into words to gain the +reputation of being excellent company for the remainder of your +existence." + + + +X. THE ADVENTURE OF THE NOBLE BACHELOR + +The Lord St. Simon marriage, and its curious termination, have +long ceased to be a subject of interest in those exalted circles +in which the unfortunate bridegroom moves. Fresh scandals have +eclipsed it, and their more piquant details have drawn the +gossips away from this four-year-old drama. As I have reason to +believe, however, that the full facts have never been revealed to +the general public, and as my friend Sherlock Holmes had a +considerable share in clearing the matter up, I feel that no +memoir of him would be complete without some little sketch of +this remarkable episode. + +It was a few weeks before my own marriage, during the days when I +was still sharing rooms with Holmes in Baker Street, that he came +home from an afternoon stroll to find a letter on the table +waiting for him. I had remained indoors all day, for the weather +had taken a sudden turn to rain, with high autumnal winds, and +the Jezail bullet which I had brought back in one of my limbs as +a relic of my Afghan campaign throbbed with dull persistence. +With my body in one easy-chair and my legs upon another, I had +surrounded myself with a cloud of newspapers until at last, +saturated with the news of the day, I tossed them all aside and +lay listless, watching the huge crest and monogram upon the +envelope upon the table and wondering lazily who my friend's +noble correspondent could be. + +"Here is a very fashionable epistle," I remarked as he entered. +"Your morning letters, if I remember right, were from a +fish-monger and a tide-waiter." + +"Yes, my correspondence has certainly the charm of variety," he +answered, smiling, "and the humbler are usually the more +interesting. This looks like one of those unwelcome social +summonses which call upon a man either to be bored or to lie." + +He broke the seal and glanced over the contents. + +"Oh, come, it may prove to be something of interest, after all." + +"Not social, then?" + +"No, distinctly professional." + +"And from a noble client?" + +"One of the highest in England." + +"My dear fellow, I congratulate you." + +"I assure you, Watson, without affectation, that the status of my +client is a matter of less moment to me than the interest of his +case. It is just possible, however, that that also may not be +wanting in this new investigation. You have been reading the +papers diligently of late, have you not?" + +"It looks like it," said I ruefully, pointing to a huge bundle in +the corner. "I have had nothing else to do." + +"It is fortunate, for you will perhaps be able to post me up. I +read nothing except the criminal news and the agony column. The +latter is always instructive. But if you have followed recent +events so closely you must have read about Lord St. Simon and his +wedding?" + +"Oh, yes, with the deepest interest." + +"That is well. The letter which I hold in my hand is from Lord +St. Simon. I will read it to you, and in return you must turn +over these papers and let me have whatever bears upon the matter. +This is what he says: + +"'MY DEAR MR. SHERLOCK HOLMES:--Lord Backwater tells me that I +may place implicit reliance upon your judgment and discretion. I +have determined, therefore, to call upon you and to consult you +in reference to the very painful event which has occurred in +connection with my wedding. Mr. Lestrade, of Scotland Yard, is +acting already in the matter, but he assures me that he sees no +objection to your co-operation, and that he even thinks that +it might be of some assistance. I will call at four o'clock in +the afternoon, and, should you have any other engagement at that +time, I hope that you will postpone it, as this matter is of +paramount importance. Yours faithfully, ST. SIMON.' + +"It is dated from Grosvenor Mansions, written with a quill pen, +and the noble lord has had the misfortune to get a smear of ink +upon the outer side of his right little finger," remarked Holmes +as he folded up the epistle. + +"He says four o'clock. It is three now. He will be here in an +hour." + +"Then I have just time, with your assistance, to get clear upon +the subject. Turn over those papers and arrange the extracts in +their order of time, while I take a glance as to who our client +is." He picked a red-covered volume from a line of books of +reference beside the mantelpiece. "Here he is," said he, sitting +down and flattening it out upon his knee. "'Lord Robert Walsingham +de Vere St. Simon, second son of the Duke of Balmoral.' Hum! 'Arms: +Azure, three caltrops in chief over a fess sable. Born in 1846.' +He's forty-one years of age, which is mature for marriage. Was +Under-Secretary for the colonies in a late administration. The +Duke, his father, was at one time Secretary for Foreign Affairs. +They inherit Plantagenet blood by direct descent, and Tudor on +the distaff side. Ha! Well, there is nothing very instructive in +all this. I think that I must turn to you Watson, for something +more solid." + +"I have very little difficulty in finding what I want," said I, +"for the facts are quite recent, and the matter struck me as +remarkable. I feared to refer them to you, however, as I knew +that you had an inquiry on hand and that you disliked the +intrusion of other matters." + +"Oh, you mean the little problem of the Grosvenor Square +furniture van. That is quite cleared up now--though, indeed, it +was obvious from the first. Pray give me the results of your +newspaper selections." + +"Here is the first notice which I can find. It is in the personal +column of the Morning Post, and dates, as you see, some weeks +back: 'A marriage has been arranged,' it says, 'and will, if +rumour is correct, very shortly take place, between Lord Robert +St. Simon, second son of the Duke of Balmoral, and Miss Hatty +Doran, the only daughter of Aloysius Doran. Esq., of San +Francisco, Cal., U.S.A.' That is all." + +"Terse and to the point," remarked Holmes, stretching his long, +thin legs towards the fire. + +"There was a paragraph amplifying this in one of the society +papers of the same week. Ah, here it is: 'There will soon be a +call for protection in the marriage market, for the present +free-trade principle appears to tell heavily against our home +product. One by one the management of the noble houses of Great +Britain is passing into the hands of our fair cousins from across +the Atlantic. An important addition has been made during the last +week to the list of the prizes which have been borne away by +these charming invaders. Lord St. Simon, who has shown himself +for over twenty years proof against the little god's arrows, has +now definitely announced his approaching marriage with Miss Hatty +Doran, the fascinating daughter of a California millionaire. Miss +Doran, whose graceful figure and striking face attracted much +attention at the Westbury House festivities, is an only child, +and it is currently reported that her dowry will run to +considerably over the six figures, with expectancies for the +future. As it is an open secret that the Duke of Balmoral has +been compelled to sell his pictures within the last few years, +and as Lord St. Simon has no property of his own save the small +estate of Birchmoor, it is obvious that the Californian heiress +is not the only gainer by an alliance which will enable her to +make the easy and common transition from a Republican lady to a +British peeress.'" + +"Anything else?" asked Holmes, yawning. + +"Oh, yes; plenty. Then there is another note in the Morning Post +to say that the marriage would be an absolutely quiet one, that it +would be at St. George's, Hanover Square, that only half a dozen +intimate friends would be invited, and that the party would +return to the furnished house at Lancaster Gate which has been +taken by Mr. Aloysius Doran. Two days later--that is, on +Wednesday last--there is a curt announcement that the wedding had +taken place, and that the honeymoon would be passed at Lord +Backwater's place, near Petersfield. Those are all the notices +which appeared before the disappearance of the bride." + +"Before the what?" asked Holmes with a start. + +"The vanishing of the lady." + +"When did she vanish, then?" + +"At the wedding breakfast." + +"Indeed. This is more interesting than it promised to be; quite +dramatic, in fact." + +"Yes; it struck me as being a little out of the common." + +"They often vanish before the ceremony, and occasionally during +the honeymoon; but I cannot call to mind anything quite so prompt +as this. Pray let me have the details." + +"I warn you that they are very incomplete." + +"Perhaps we may make them less so." + +"Such as they are, they are set forth in a single article of a +morning paper of yesterday, which I will read to you. It is +headed, 'Singular Occurrence at a Fashionable Wedding': + +"'The family of Lord Robert St. Simon has been thrown into the +greatest consternation by the strange and painful episodes which +have taken place in connection with his wedding. The ceremony, as +shortly announced in the papers of yesterday, occurred on the +previous morning; but it is only now that it has been possible to +confirm the strange rumours which have been so persistently +floating about. In spite of the attempts of the friends to hush +the matter up, so much public attention has now been drawn to it +that no good purpose can be served by affecting to disregard what +is a common subject for conversation. + +"'The ceremony, which was performed at St. George's, Hanover +Square, was a very quiet one, no one being present save the +father of the bride, Mr. Aloysius Doran, the Duchess of Balmoral, +Lord Backwater, Lord Eustace and Lady Clara St. Simon (the +younger brother and sister of the bridegroom), and Lady Alicia +Whittington. The whole party proceeded afterwards to the house of +Mr. Aloysius Doran, at Lancaster Gate, where breakfast had been +prepared. It appears that some little trouble was caused by a +woman, whose name has not been ascertained, who endeavoured to +force her way into the house after the bridal party, alleging +that she had some claim upon Lord St. Simon. It was only after a +painful and prolonged scene that she was ejected by the butler +and the footman. The bride, who had fortunately entered the house +before this unpleasant interruption, had sat down to breakfast +with the rest, when she complained of a sudden indisposition and +retired to her room. Her prolonged absence having caused some +comment, her father followed her, but learned from her maid that +she had only come up to her chamber for an instant, caught up an +ulster and bonnet, and hurried down to the passage. One of the +footmen declared that he had seen a lady leave the house thus +apparelled, but had refused to credit that it was his mistress, +believing her to be with the company. On ascertaining that his +daughter had disappeared, Mr. Aloysius Doran, in conjunction with +the bridegroom, instantly put themselves in communication with +the police, and very energetic inquiries are being made, which +will probably result in a speedy clearing up of this very +singular business. Up to a late hour last night, however, nothing +had transpired as to the whereabouts of the missing lady. There +are rumours of foul play in the matter, and it is said that the +police have caused the arrest of the woman who had caused the +original disturbance, in the belief that, from jealousy or some +other motive, she may have been concerned in the strange +disappearance of the bride.'" + +"And is that all?" + +"Only one little item in another of the morning papers, but it is +a suggestive one." + +"And it is--" + +"That Miss Flora Millar, the lady who had caused the disturbance, +has actually been arrested. It appears that she was formerly a +danseuse at the Allegro, and that she has known the bridegroom +for some years. There are no further particulars, and the whole +case is in your hands now--so far as it has been set forth in the +public press." + +"And an exceedingly interesting case it appears to be. I would +not have missed it for worlds. But there is a ring at the bell, +Watson, and as the clock makes it a few minutes after four, I +have no doubt that this will prove to be our noble client. Do not +dream of going, Watson, for I very much prefer having a witness, +if only as a check to my own memory." + +"Lord Robert St. Simon," announced our page-boy, throwing open +the door. A gentleman entered, with a pleasant, cultured face, +high-nosed and pale, with something perhaps of petulance about +the mouth, and with the steady, well-opened eye of a man whose +pleasant lot it had ever been to command and to be obeyed. His +manner was brisk, and yet his general appearance gave an undue +impression of age, for he had a slight forward stoop and a little +bend of the knees as he walked. His hair, too, as he swept off +his very curly-brimmed hat, was grizzled round the edges and thin +upon the top. As to his dress, it was careful to the verge of +foppishness, with high collar, black frock-coat, white waistcoat, +yellow gloves, patent-leather shoes, and light-coloured gaiters. +He advanced slowly into the room, turning his head from left to +right, and swinging in his right hand the cord which held his +golden eyeglasses. + +"Good-day, Lord St. Simon," said Holmes, rising and bowing. "Pray +take the basket-chair. This is my friend and colleague, Dr. +Watson. Draw up a little to the fire, and we will talk this +matter over." + +"A most painful matter to me, as you can most readily imagine, +Mr. Holmes. I have been cut to the quick. I understand that you +have already managed several delicate cases of this sort, sir, +though I presume that they were hardly from the same class of +society." + +"No, I am descending." + +"I beg pardon." + +"My last client of the sort was a king." + +"Oh, really! I had no idea. And which king?" + +"The King of Scandinavia." + +"What! Had he lost his wife?" + +"You can understand," said Holmes suavely, "that I extend to the +affairs of my other clients the same secrecy which I promise to +you in yours." + +"Of course! Very right! very right! I'm sure I beg pardon. As to +my own case, I am ready to give you any information which may +assist you in forming an opinion." + +"Thank you. I have already learned all that is in the public +prints, nothing more. I presume that I may take it as correct--this +article, for example, as to the disappearance of the bride." + +Lord St. Simon glanced over it. "Yes, it is correct, as far as it +goes." + +"But it needs a great deal of supplementing before anyone could +offer an opinion. I think that I may arrive at my facts most +directly by questioning you." + +"Pray do so." + +"When did you first meet Miss Hatty Doran?" + +"In San Francisco, a year ago." + +"You were travelling in the States?" + +"Yes." + +"Did you become engaged then?" + +"No." + +"But you were on a friendly footing?" + +"I was amused by her society, and she could see that I was +amused." + +"Her father is very rich?" + +"He is said to be the richest man on the Pacific slope." + +"And how did he make his money?" + +"In mining. He had nothing a few years ago. Then he struck gold, +invested it, and came up by leaps and bounds." + +"Now, what is your own impression as to the young lady's--your +wife's character?" + +The nobleman swung his glasses a little faster and stared down +into the fire. "You see, Mr. Holmes," said he, "my wife was +twenty before her father became a rich man. During that time she +ran free in a mining camp and wandered through woods or +mountains, so that her education has come from Nature rather than +from the schoolmaster. She is what we call in England a tomboy, +with a strong nature, wild and free, unfettered by any sort of +traditions. She is impetuous--volcanic, I was about to say. She +is swift in making up her mind and fearless in carrying out her +resolutions. On the other hand, I would not have given her the +name which I have the honour to bear"--he gave a little stately +cough--"had not I thought her to be at bottom a noble woman. I +believe that she is capable of heroic self-sacrifice and that +anything dishonourable would be repugnant to her." + +"Have you her photograph?" + +"I brought this with me." He opened a locket and showed us the +full face of a very lovely woman. It was not a photograph but an +ivory miniature, and the artist had brought out the full effect +of the lustrous black hair, the large dark eyes, and the +exquisite mouth. Holmes gazed long and earnestly at it. Then he +closed the locket and handed it back to Lord St. Simon. + +"The young lady came to London, then, and you renewed your +acquaintance?" + +"Yes, her father brought her over for this last London season. I +met her several times, became engaged to her, and have now +married her." + +"She brought, I understand, a considerable dowry?" + +"A fair dowry. Not more than is usual in my family." + +"And this, of course, remains to you, since the marriage is a +fait accompli?" + +"I really have made no inquiries on the subject." + +"Very naturally not. Did you see Miss Doran on the day before the +wedding?" + +"Yes." + +"Was she in good spirits?" + +"Never better. She kept talking of what we should do in our +future lives." + +"Indeed! That is very interesting. And on the morning of the +wedding?" + +"She was as bright as possible--at least until after the +ceremony." + +"And did you observe any change in her then?" + +"Well, to tell the truth, I saw then the first signs that I had +ever seen that her temper was just a little sharp. The incident +however, was too trivial to relate and can have no possible +bearing upon the case." + +"Pray let us have it, for all that." + +"Oh, it is childish. She dropped her bouquet as we went towards +the vestry. She was passing the front pew at the time, and it +fell over into the pew. There was a moment's delay, but the +gentleman in the pew handed it up to her again, and it did not +appear to be the worse for the fall. Yet when I spoke to her of +the matter, she answered me abruptly; and in the carriage, on our +way home, she seemed absurdly agitated over this trifling cause." + +"Indeed! You say that there was a gentleman in the pew. Some of +the general public were present, then?" + +"Oh, yes. It is impossible to exclude them when the church is +open." + +"This gentleman was not one of your wife's friends?" + +"No, no; I call him a gentleman by courtesy, but he was quite a +common-looking person. I hardly noticed his appearance. But +really I think that we are wandering rather far from the point." + +"Lady St. Simon, then, returned from the wedding in a less +cheerful frame of mind than she had gone to it. What did she do +on re-entering her father's house?" + +"I saw her in conversation with her maid." + +"And who is her maid?" + +"Alice is her name. She is an American and came from California +with her." + +"A confidential servant?" + +"A little too much so. It seemed to me that her mistress allowed +her to take great liberties. Still, of course, in America they +look upon these things in a different way." + +"How long did she speak to this Alice?" + +"Oh, a few minutes. I had something else to think of." + +"You did not overhear what they said?" + +"Lady St. Simon said something about 'jumping a claim.' She was +accustomed to use slang of the kind. I have no idea what she +meant." + +"American slang is very expressive sometimes. And what did your +wife do when she finished speaking to her maid?" + +"She walked into the breakfast-room." + +"On your arm?" + +"No, alone. She was very independent in little matters like that. +Then, after we had sat down for ten minutes or so, she rose +hurriedly, muttered some words of apology, and left the room. She +never came back." + +"But this maid, Alice, as I understand, deposes that she went to +her room, covered her bride's dress with a long ulster, put on a +bonnet, and went out." + +"Quite so. And she was afterwards seen walking into Hyde Park in +company with Flora Millar, a woman who is now in custody, and who +had already made a disturbance at Mr. Doran's house that +morning." + +"Ah, yes. I should like a few particulars as to this young lady, +and your relations to her." + +Lord St. Simon shrugged his shoulders and raised his eyebrows. +"We have been on a friendly footing for some years--I may say on +a very friendly footing. She used to be at the Allegro. I have +not treated her ungenerously, and she had no just cause of +complaint against me, but you know what women are, Mr. Holmes. +Flora was a dear little thing, but exceedingly hot-headed and +devotedly attached to me. She wrote me dreadful letters when she +heard that I was about to be married, and, to tell the truth, the +reason why I had the marriage celebrated so quietly was that I +feared lest there might be a scandal in the church. She came to +Mr. Doran's door just after we returned, and she endeavoured to +push her way in, uttering very abusive expressions towards my +wife, and even threatening her, but I had foreseen the +possibility of something of the sort, and I had two police +fellows there in private clothes, who soon pushed her out again. +She was quiet when she saw that there was no good in making a +row." + +"Did your wife hear all this?" + +"No, thank goodness, she did not." + +"And she was seen walking with this very woman afterwards?" + +"Yes. That is what Mr. Lestrade, of Scotland Yard, looks upon as +so serious. It is thought that Flora decoyed my wife out and laid +some terrible trap for her." + +"Well, it is a possible supposition." + +"You think so, too?" + +"I did not say a probable one. But you do not yourself look upon +this as likely?" + +"I do not think Flora would hurt a fly." + +"Still, jealousy is a strange transformer of characters. Pray +what is your own theory as to what took place?" + +"Well, really, I came to seek a theory, not to propound one. I +have given you all the facts. Since you ask me, however, I may +say that it has occurred to me as possible that the excitement of +this affair, the consciousness that she had made so immense a +social stride, had the effect of causing some little nervous +disturbance in my wife." + +"In short, that she had become suddenly deranged?" + +"Well, really, when I consider that she has turned her back--I +will not say upon me, but upon so much that many have aspired to +without success--I can hardly explain it in any other fashion." + +"Well, certainly that is also a conceivable hypothesis," said +Holmes, smiling. "And now, Lord St. Simon, I think that I have +nearly all my data. May I ask whether you were seated at the +breakfast-table so that you could see out of the window?" + +"We could see the other side of the road and the Park." + +"Quite so. Then I do not think that I need to detain you longer. +I shall communicate with you." + +"Should you be fortunate enough to solve this problem," said our +client, rising. + +"I have solved it." + +"Eh? What was that?" + +"I say that I have solved it." + +"Where, then, is my wife?" + +"That is a detail which I shall speedily supply." + +Lord St. Simon shook his head. "I am afraid that it will take +wiser heads than yours or mine," he remarked, and bowing in a +stately, old-fashioned manner he departed. + +"It is very good of Lord St. Simon to honour my head by putting +it on a level with his own," said Sherlock Holmes, laughing. "I +think that I shall have a whisky and soda and a cigar after all +this cross-questioning. I had formed my conclusions as to the +case before our client came into the room." + +"My dear Holmes!" + +"I have notes of several similar cases, though none, as I +remarked before, which were quite as prompt. My whole examination +served to turn my conjecture into a certainty. Circumstantial +evidence is occasionally very convincing, as when you find a +trout in the milk, to quote Thoreau's example." + +"But I have heard all that you have heard." + +"Without, however, the knowledge of pre-existing cases which +serves me so well. There was a parallel instance in Aberdeen some +years back, and something on very much the same lines at Munich +the year after the Franco-Prussian War. It is one of these +cases--but, hullo, here is Lestrade! Good-afternoon, Lestrade! +You will find an extra tumbler upon the sideboard, and there are +cigars in the box." + +The official detective was attired in a pea-jacket and cravat, +which gave him a decidedly nautical appearance, and he carried a +black canvas bag in his hand. With a short greeting he seated +himself and lit the cigar which had been offered to him. + +"What's up, then?" asked Holmes with a twinkle in his eye. "You +look dissatisfied." + +"And I feel dissatisfied. It is this infernal St. Simon marriage +case. I can make neither head nor tail of the business." + +"Really! You surprise me." + +"Who ever heard of such a mixed affair? Every clue seems to slip +through my fingers. I have been at work upon it all day." + +"And very wet it seems to have made you," said Holmes laying his +hand upon the arm of the pea-jacket. + +"Yes, I have been dragging the Serpentine." + +"In heaven's name, what for?" + +"In search of the body of Lady St. Simon." + +Sherlock Holmes leaned back in his chair and laughed heartily. + +"Have you dragged the basin of Trafalgar Square fountain?" he +asked. + +"Why? What do you mean?" + +"Because you have just as good a chance of finding this lady in +the one as in the other." + +Lestrade shot an angry glance at my companion. "I suppose you +know all about it," he snarled. + +"Well, I have only just heard the facts, but my mind is made up." + +"Oh, indeed! Then you think that the Serpentine plays no part in +the matter?" + +"I think it very unlikely." + +"Then perhaps you will kindly explain how it is that we found +this in it?" He opened his bag as he spoke, and tumbled onto the +floor a wedding-dress of watered silk, a pair of white satin +shoes and a bride's wreath and veil, all discoloured and soaked +in water. "There," said he, putting a new wedding-ring upon the +top of the pile. "There is a little nut for you to crack, Master +Holmes." + +"Oh, indeed!" said my friend, blowing blue rings into the air. +"You dragged them from the Serpentine?" + +"No. They were found floating near the margin by a park-keeper. +They have been identified as her clothes, and it seemed to me +that if the clothes were there the body would not be far off." + +"By the same brilliant reasoning, every man's body is to be found +in the neighbourhood of his wardrobe. And pray what did you hope +to arrive at through this?" + +"At some evidence implicating Flora Millar in the disappearance." + +"I am afraid that you will find it difficult." + +"Are you, indeed, now?" cried Lestrade with some bitterness. "I +am afraid, Holmes, that you are not very practical with your +deductions and your inferences. You have made two blunders in as +many minutes. This dress does implicate Miss Flora Millar." + +"And how?" + +"In the dress is a pocket. In the pocket is a card-case. In the +card-case is a note. And here is the very note." He slapped it +down upon the table in front of him. "Listen to this: 'You will +see me when all is ready. Come at once. F.H.M.' Now my theory all +along has been that Lady St. Simon was decoyed away by Flora +Millar, and that she, with confederates, no doubt, was +responsible for her disappearance. Here, signed with her +initials, is the very note which was no doubt quietly slipped +into her hand at the door and which lured her within their +reach." + +"Very good, Lestrade," said Holmes, laughing. "You really are +very fine indeed. Let me see it." He took up the paper in a +listless way, but his attention instantly became riveted, and he +gave a little cry of satisfaction. "This is indeed important," +said he. + +"Ha! you find it so?" + +"Extremely so. I congratulate you warmly." + +Lestrade rose in his triumph and bent his head to look. "Why," he +shrieked, "you're looking at the wrong side!" + +"On the contrary, this is the right side." + +"The right side? You're mad! Here is the note written in pencil +over here." + +"And over here is what appears to be the fragment of a hotel +bill, which interests me deeply." + +"There's nothing in it. I looked at it before," said Lestrade. +"'Oct. 4th, rooms 8s., breakfast 2s. 6d., cocktail 1s., lunch 2s. +6d., glass sherry, 8d.' I see nothing in that." + +"Very likely not. It is most important, all the same. As to the +note, it is important also, or at least the initials are, so I +congratulate you again." + +"I've wasted time enough," said Lestrade, rising. "I believe in +hard work and not in sitting by the fire spinning fine theories. +Good-day, Mr. Holmes, and we shall see which gets to the bottom +of the matter first." He gathered up the garments, thrust them +into the bag, and made for the door. + +"Just one hint to you, Lestrade," drawled Holmes before his rival +vanished; "I will tell you the true solution of the matter. Lady +St. Simon is a myth. There is not, and there never has been, any +such person." + +Lestrade looked sadly at my companion. Then he turned to me, +tapped his forehead three times, shook his head solemnly, and +hurried away. + +He had hardly shut the door behind him when Holmes rose to put on +his overcoat. "There is something in what the fellow says about +outdoor work," he remarked, "so I think, Watson, that I must +leave you to your papers for a little." + +It was after five o'clock when Sherlock Holmes left me, but I had +no time to be lonely, for within an hour there arrived a +confectioner's man with a very large flat box. This he unpacked +with the help of a youth whom he had brought with him, and +presently, to my very great astonishment, a quite epicurean +little cold supper began to be laid out upon our humble +lodging-house mahogany. There were a couple of brace of cold +woodcock, a pheasant, a pâté de foie gras pie with a group of +ancient and cobwebby bottles. Having laid out all these luxuries, +my two visitors vanished away, like the genii of the Arabian +Nights, with no explanation save that the things had been paid +for and were ordered to this address. + +Just before nine o'clock Sherlock Holmes stepped briskly into the +room. His features were gravely set, but there was a light in his +eye which made me think that he had not been disappointed in his +conclusions. + +"They have laid the supper, then," he said, rubbing his hands. + +"You seem to expect company. They have laid for five." + +"Yes, I fancy we may have some company dropping in," said he. "I +am surprised that Lord St. Simon has not already arrived. Ha! I +fancy that I hear his step now upon the stairs." + +It was indeed our visitor of the afternoon who came bustling in, +dangling his glasses more vigorously than ever, and with a very +perturbed expression upon his aristocratic features. + +"My messenger reached you, then?" asked Holmes. + +"Yes, and I confess that the contents startled me beyond measure. +Have you good authority for what you say?" + +"The best possible." + +Lord St. Simon sank into a chair and passed his hand over his +forehead. + +"What will the Duke say," he murmured, "when he hears that one of +the family has been subjected to such humiliation?" + +"It is the purest accident. I cannot allow that there is any +humiliation." + +"Ah, you look on these things from another standpoint." + +"I fail to see that anyone is to blame. I can hardly see how the +lady could have acted otherwise, though her abrupt method of +doing it was undoubtedly to be regretted. Having no mother, she +had no one to advise her at such a crisis." + +"It was a slight, sir, a public slight," said Lord St. Simon, +tapping his fingers upon the table. + +"You must make allowance for this poor girl, placed in so +unprecedented a position." + +"I will make no allowance. I am very angry indeed, and I have +been shamefully used." + +"I think that I heard a ring," said Holmes. "Yes, there are steps +on the landing. If I cannot persuade you to take a lenient view +of the matter, Lord St. Simon, I have brought an advocate here +who may be more successful." He opened the door and ushered in a +lady and gentleman. "Lord St. Simon," said he "allow me to +introduce you to Mr. and Mrs. Francis Hay Moulton. The lady, I +think, you have already met." + +At the sight of these newcomers our client had sprung from his +seat and stood very erect, with his eyes cast down and his hand +thrust into the breast of his frock-coat, a picture of offended +dignity. The lady had taken a quick step forward and had held out +her hand to him, but he still refused to raise his eyes. It was +as well for his resolution, perhaps, for her pleading face was +one which it was hard to resist. + +"You're angry, Robert," said she. "Well, I guess you have every +cause to be." + +"Pray make no apology to me," said Lord St. Simon bitterly. + +"Oh, yes, I know that I have treated you real bad and that I +should have spoken to you before I went; but I was kind of +rattled, and from the time when I saw Frank here again I just +didn't know what I was doing or saying. I only wonder I didn't +fall down and do a faint right there before the altar." + +"Perhaps, Mrs. Moulton, you would like my friend and me to leave +the room while you explain this matter?" + +"If I may give an opinion," remarked the strange gentleman, +"we've had just a little too much secrecy over this business +already. For my part, I should like all Europe and America to +hear the rights of it." He was a small, wiry, sunburnt man, +clean-shaven, with a sharp face and alert manner. + +"Then I'll tell our story right away," said the lady. "Frank here +and I met in '84, in McQuire's camp, near the Rockies, where pa +was working a claim. We were engaged to each other, Frank and I; +but then one day father struck a rich pocket and made a pile, +while poor Frank here had a claim that petered out and came to +nothing. The richer pa grew the poorer was Frank; so at last pa +wouldn't hear of our engagement lasting any longer, and he took +me away to 'Frisco. Frank wouldn't throw up his hand, though; so +he followed me there, and he saw me without pa knowing anything +about it. It would only have made him mad to know, so we just +fixed it all up for ourselves. Frank said that he would go and +make his pile, too, and never come back to claim me until he had +as much as pa. So then I promised to wait for him to the end of +time and pledged myself not to marry anyone else while he lived. +'Why shouldn't we be married right away, then,' said he, 'and +then I will feel sure of you; and I won't claim to be your +husband until I come back?' Well, we talked it over, and he had +fixed it all up so nicely, with a clergyman all ready in waiting, +that we just did it right there; and then Frank went off to seek +his fortune, and I went back to pa. + +"The next I heard of Frank was that he was in Montana, and then +he went prospecting in Arizona, and then I heard of him from New +Mexico. After that came a long newspaper story about how a +miners' camp had been attacked by Apache Indians, and there was +my Frank's name among the killed. I fainted dead away, and I was +very sick for months after. Pa thought I had a decline and took +me to half the doctors in 'Frisco. Not a word of news came for a +year and more, so that I never doubted that Frank was really +dead. Then Lord St. Simon came to 'Frisco, and we came to London, +and a marriage was arranged, and pa was very pleased, but I felt +all the time that no man on this earth would ever take the place +in my heart that had been given to my poor Frank. + +"Still, if I had married Lord St. Simon, of course I'd have done +my duty by him. We can't command our love, but we can our +actions. I went to the altar with him with the intention to make +him just as good a wife as it was in me to be. But you may +imagine what I felt when, just as I came to the altar rails, I +glanced back and saw Frank standing and looking at me out of the +first pew. I thought it was his ghost at first; but when I looked +again there he was still, with a kind of question in his eyes, as +if to ask me whether I were glad or sorry to see him. I wonder I +didn't drop. I know that everything was turning round, and the +words of the clergyman were just like the buzz of a bee in my +ear. I didn't know what to do. Should I stop the service and make +a scene in the church? I glanced at him again, and he seemed to +know what I was thinking, for he raised his finger to his lips to +tell me to be still. Then I saw him scribble on a piece of paper, +and I knew that he was writing me a note. As I passed his pew on +the way out I dropped my bouquet over to him, and he slipped the +note into my hand when he returned me the flowers. It was only a +line asking me to join him when he made the sign to me to do so. +Of course I never doubted for a moment that my first duty was now +to him, and I determined to do just whatever he might direct. + +"When I got back I told my maid, who had known him in California, +and had always been his friend. I ordered her to say nothing, but +to get a few things packed and my ulster ready. I know I ought to +have spoken to Lord St. Simon, but it was dreadful hard before +his mother and all those great people. I just made up my mind to +run away and explain afterwards. I hadn't been at the table ten +minutes before I saw Frank out of the window at the other side of +the road. He beckoned to me and then began walking into the Park. +I slipped out, put on my things, and followed him. Some woman +came talking something or other about Lord St. Simon to +me--seemed to me from the little I heard as if he had a little +secret of his own before marriage also--but I managed to get away +from her and soon overtook Frank. We got into a cab together, and +away we drove to some lodgings he had taken in Gordon Square, and +that was my true wedding after all those years of waiting. Frank +had been a prisoner among the Apaches, had escaped, came on to +'Frisco, found that I had given him up for dead and had gone to +England, followed me there, and had come upon me at last on the +very morning of my second wedding." + +"I saw it in a paper," explained the American. "It gave the name +and the church but not where the lady lived." + +"Then we had a talk as to what we should do, and Frank was all +for openness, but I was so ashamed of it all that I felt as if I +should like to vanish away and never see any of them again--just +sending a line to pa, perhaps, to show him that I was alive. It +was awful to me to think of all those lords and ladies sitting +round that breakfast-table and waiting for me to come back. So +Frank took my wedding-clothes and things and made a bundle of +them, so that I should not be traced, and dropped them away +somewhere where no one could find them. It is likely that we +should have gone on to Paris to-morrow, only that this good +gentleman, Mr. Holmes, came round to us this evening, though how +he found us is more than I can think, and he showed us very +clearly and kindly that I was wrong and that Frank was right, and +that we should be putting ourselves in the wrong if we were so +secret. Then he offered to give us a chance of talking to Lord +St. Simon alone, and so we came right away round to his rooms at +once. Now, Robert, you have heard it all, and I am very sorry if +I have given you pain, and I hope that you do not think very +meanly of me." + +Lord St. Simon had by no means relaxed his rigid attitude, but +had listened with a frowning brow and a compressed lip to this +long narrative. + +"Excuse me," he said, "but it is not my custom to discuss my most +intimate personal affairs in this public manner." + +"Then you won't forgive me? You won't shake hands before I go?" + +"Oh, certainly, if it would give you any pleasure." He put out +his hand and coldly grasped that which she extended to him. + +"I had hoped," suggested Holmes, "that you would have joined us +in a friendly supper." + +"I think that there you ask a little too much," responded his +Lordship. "I may be forced to acquiesce in these recent +developments, but I can hardly be expected to make merry over +them. I think that with your permission I will now wish you all a +very good-night." He included us all in a sweeping bow and +stalked out of the room. + +"Then I trust that you at least will honour me with your +company," said Sherlock Holmes. "It is always a joy to meet an +American, Mr. Moulton, for I am one of those who believe that the +folly of a monarch and the blundering of a minister in far-gone +years will not prevent our children from being some day citizens +of the same world-wide country under a flag which shall be a +quartering of the Union Jack with the Stars and Stripes." + +"The case has been an interesting one," remarked Holmes when our +visitors had left us, "because it serves to show very clearly how +simple the explanation may be of an affair which at first sight +seems to be almost inexplicable. Nothing could be more natural +than the sequence of events as narrated by this lady, and nothing +stranger than the result when viewed, for instance, by Mr. +Lestrade of Scotland Yard." + +"You were not yourself at fault at all, then?" + +"From the first, two facts were very obvious to me, the one that +the lady had been quite willing to undergo the wedding ceremony, +the other that she had repented of it within a few minutes of +returning home. Obviously something had occurred during the +morning, then, to cause her to change her mind. What could that +something be? She could not have spoken to anyone when she was +out, for she had been in the company of the bridegroom. Had she +seen someone, then? If she had, it must be someone from America +because she had spent so short a time in this country that she +could hardly have allowed anyone to acquire so deep an influence +over her that the mere sight of him would induce her to change +her plans so completely. You see we have already arrived, by a +process of exclusion, at the idea that she might have seen an +American. Then who could this American be, and why should he +possess so much influence over her? It might be a lover; it might +be a husband. Her young womanhood had, I knew, been spent in +rough scenes and under strange conditions. So far I had got +before I ever heard Lord St. Simon's narrative. When he told us +of a man in a pew, of the change in the bride's manner, of so +transparent a device for obtaining a note as the dropping of a +bouquet, of her resort to her confidential maid, and of her very +significant allusion to claim-jumping--which in miners' parlance +means taking possession of that which another person has a prior +claim to--the whole situation became absolutely clear. She had +gone off with a man, and the man was either a lover or was a +previous husband--the chances being in favour of the latter." + +"And how in the world did you find them?" + +"It might have been difficult, but friend Lestrade held +information in his hands the value of which he did not himself +know. The initials were, of course, of the highest importance, +but more valuable still was it to know that within a week he had +settled his bill at one of the most select London hotels." + +"How did you deduce the select?" + +"By the select prices. Eight shillings for a bed and eightpence +for a glass of sherry pointed to one of the most expensive +hotels. There are not many in London which charge at that rate. +In the second one which I visited in Northumberland Avenue, I +learned by an inspection of the book that Francis H. Moulton, an +American gentleman, had left only the day before, and on looking +over the entries against him, I came upon the very items which I +had seen in the duplicate bill. His letters were to be forwarded +to 226 Gordon Square; so thither I travelled, and being fortunate +enough to find the loving couple at home, I ventured to give them +some paternal advice and to point out to them that it would be +better in every way that they should make their position a little +clearer both to the general public and to Lord St. Simon in +particular. I invited them to meet him here, and, as you see, I +made him keep the appointment." + +"But with no very good result," I remarked. "His conduct was +certainly not very gracious." + +"Ah, Watson," said Holmes, smiling, "perhaps you would not be +very gracious either, if, after all the trouble of wooing and +wedding, you found yourself deprived in an instant of wife and of +fortune. I think that we may judge Lord St. Simon very mercifully +and thank our stars that we are never likely to find ourselves in +the same position. Draw your chair up and hand me my violin, for +the only problem we have still to solve is how to while away +these bleak autumnal evenings." + + + +XI. THE ADVENTURE OF THE BERYL CORONET + +"Holmes," said I as I stood one morning in our bow-window looking +down the street, "here is a madman coming along. It seems rather +sad that his relatives should allow him to come out alone." + +My friend rose lazily from his armchair and stood with his hands +in the pockets of his dressing-gown, looking over my shoulder. It +was a bright, crisp February morning, and the snow of the day +before still lay deep upon the ground, shimmering brightly in the +wintry sun. Down the centre of Baker Street it had been ploughed +into a brown crumbly band by the traffic, but at either side and +on the heaped-up edges of the foot-paths it still lay as white as +when it fell. The grey pavement had been cleaned and scraped, but +was still dangerously slippery, so that there were fewer +passengers than usual. Indeed, from the direction of the +Metropolitan Station no one was coming save the single gentleman +whose eccentric conduct had drawn my attention. + +He was a man of about fifty, tall, portly, and imposing, with a +massive, strongly marked face and a commanding figure. He was +dressed in a sombre yet rich style, in black frock-coat, shining +hat, neat brown gaiters, and well-cut pearl-grey trousers. Yet +his actions were in absurd contrast to the dignity of his dress +and features, for he was running hard, with occasional little +springs, such as a weary man gives who is little accustomed to +set any tax upon his legs. As he ran he jerked his hands up and +down, waggled his head, and writhed his face into the most +extraordinary contortions. + +"What on earth can be the matter with him?" I asked. "He is +looking up at the numbers of the houses." + +"I believe that he is coming here," said Holmes, rubbing his +hands. + +"Here?" + +"Yes; I rather think he is coming to consult me professionally. I +think that I recognise the symptoms. Ha! did I not tell you?" As +he spoke, the man, puffing and blowing, rushed at our door and +pulled at our bell until the whole house resounded with the +clanging. + +A few moments later he was in our room, still puffing, still +gesticulating, but with so fixed a look of grief and despair in +his eyes that our smiles were turned in an instant to horror and +pity. For a while he could not get his words out, but swayed his +body and plucked at his hair like one who has been driven to the +extreme limits of his reason. Then, suddenly springing to his +feet, he beat his head against the wall with such force that we +both rushed upon him and tore him away to the centre of the room. +Sherlock Holmes pushed him down into the easy-chair and, sitting +beside him, patted his hand and chatted with him in the easy, +soothing tones which he knew so well how to employ. + +"You have come to me to tell your story, have you not?" said he. +"You are fatigued with your haste. Pray wait until you have +recovered yourself, and then I shall be most happy to look into +any little problem which you may submit to me." + +The man sat for a minute or more with a heaving chest, fighting +against his emotion. Then he passed his handkerchief over his +brow, set his lips tight, and turned his face towards us. + +"No doubt you think me mad?" said he. + +"I see that you have had some great trouble," responded Holmes. + +"God knows I have!--a trouble which is enough to unseat my +reason, so sudden and so terrible is it. Public disgrace I might +have faced, although I am a man whose character has never yet +borne a stain. Private affliction also is the lot of every man; +but the two coming together, and in so frightful a form, have +been enough to shake my very soul. Besides, it is not I alone. +The very noblest in the land may suffer unless some way be found +out of this horrible affair." + +"Pray compose yourself, sir," said Holmes, "and let me have a +clear account of who you are and what it is that has befallen +you." + +"My name," answered our visitor, "is probably familiar to your +ears. I am Alexander Holder, of the banking firm of Holder & +Stevenson, of Threadneedle Street." + +The name was indeed well known to us as belonging to the senior +partner in the second largest private banking concern in the City +of London. What could have happened, then, to bring one of the +foremost citizens of London to this most pitiable pass? We +waited, all curiosity, until with another effort he braced +himself to tell his story. + +"I feel that time is of value," said he; "that is why I hastened +here when the police inspector suggested that I should secure +your co-operation. I came to Baker Street by the Underground and +hurried from there on foot, for the cabs go slowly through this +snow. That is why I was so out of breath, for I am a man who +takes very little exercise. I feel better now, and I will put the +facts before you as shortly and yet as clearly as I can. + +"It is, of course, well known to you that in a successful banking +business as much depends upon our being able to find remunerative +investments for our funds as upon our increasing our connection +and the number of our depositors. One of our most lucrative means +of laying out money is in the shape of loans, where the security +is unimpeachable. We have done a good deal in this direction +during the last few years, and there are many noble families to +whom we have advanced large sums upon the security of their +pictures, libraries, or plate. + +"Yesterday morning I was seated in my office at the bank when a +card was brought in to me by one of the clerks. I started when I +saw the name, for it was that of none other than--well, perhaps +even to you I had better say no more than that it was a name +which is a household word all over the earth--one of the highest, +noblest, most exalted names in England. I was overwhelmed by the +honour and attempted, when he entered, to say so, but he plunged +at once into business with the air of a man who wishes to hurry +quickly through a disagreeable task. + +"'Mr. Holder,' said he, 'I have been informed that you are in the +habit of advancing money.' + +"'The firm does so when the security is good.' I answered. + +"'It is absolutely essential to me,' said he, 'that I should have +50,000 pounds at once. I could, of course, borrow so trifling a +sum ten times over from my friends, but I much prefer to make it +a matter of business and to carry out that business myself. In my +position you can readily understand that it is unwise to place +one's self under obligations.' + +"'For how long, may I ask, do you want this sum?' I asked. + +"'Next Monday I have a large sum due to me, and I shall then most +certainly repay what you advance, with whatever interest you +think it right to charge. But it is very essential to me that the +money should be paid at once.' + +"'I should be happy to advance it without further parley from my +own private purse,' said I, 'were it not that the strain would be +rather more than it could bear. If, on the other hand, I am to do +it in the name of the firm, then in justice to my partner I must +insist that, even in your case, every businesslike precaution +should be taken.' + +"'I should much prefer to have it so,' said he, raising up a +square, black morocco case which he had laid beside his chair. +'You have doubtless heard of the Beryl Coronet?' + +"'One of the most precious public possessions of the empire,' +said I. + +"'Precisely.' He opened the case, and there, imbedded in soft, +flesh-coloured velvet, lay the magnificent piece of jewellery +which he had named. 'There are thirty-nine enormous beryls,' said +he, 'and the price of the gold chasing is incalculable. The +lowest estimate would put the worth of the coronet at double the +sum which I have asked. I am prepared to leave it with you as my +security.' + +"I took the precious case into my hands and looked in some +perplexity from it to my illustrious client. + +"'You doubt its value?' he asked. + +"'Not at all. I only doubt--' + +"'The propriety of my leaving it. You may set your mind at rest +about that. I should not dream of doing so were it not absolutely +certain that I should be able in four days to reclaim it. It is a +pure matter of form. Is the security sufficient?' + +"'Ample.' + +"'You understand, Mr. Holder, that I am giving you a strong proof +of the confidence which I have in you, founded upon all that I +have heard of you. I rely upon you not only to be discreet and to +refrain from all gossip upon the matter but, above all, to +preserve this coronet with every possible precaution because I +need not say that a great public scandal would be caused if any +harm were to befall it. Any injury to it would be almost as +serious as its complete loss, for there are no beryls in the +world to match these, and it would be impossible to replace them. +I leave it with you, however, with every confidence, and I shall +call for it in person on Monday morning.' + +"Seeing that my client was anxious to leave, I said no more but, +calling for my cashier, I ordered him to pay over fifty 1000 +pound notes. When I was alone once more, however, with the +precious case lying upon the table in front of me, I could not +but think with some misgivings of the immense responsibility +which it entailed upon me. There could be no doubt that, as it +was a national possession, a horrible scandal would ensue if any +misfortune should occur to it. I already regretted having ever +consented to take charge of it. However, it was too late to alter +the matter now, so I locked it up in my private safe and turned +once more to my work. + +"When evening came I felt that it would be an imprudence to leave +so precious a thing in the office behind me. Bankers' safes had +been forced before now, and why should not mine be? If so, how +terrible would be the position in which I should find myself! I +determined, therefore, that for the next few days I would always +carry the case backward and forward with me, so that it might +never be really out of my reach. With this intention, I called a +cab and drove out to my house at Streatham, carrying the jewel +with me. I did not breathe freely until I had taken it upstairs +and locked it in the bureau of my dressing-room. + +"And now a word as to my household, Mr. Holmes, for I wish you to +thoroughly understand the situation. My groom and my page sleep +out of the house, and may be set aside altogether. I have three +maid-servants who have been with me a number of years and whose +absolute reliability is quite above suspicion. Another, Lucy +Parr, the second waiting-maid, has only been in my service a few +months. She came with an excellent character, however, and has +always given me satisfaction. She is a very pretty girl and has +attracted admirers who have occasionally hung about the place. +That is the only drawback which we have found to her, but we +believe her to be a thoroughly good girl in every way. + +"So much for the servants. My family itself is so small that it +will not take me long to describe it. I am a widower and have an +only son, Arthur. He has been a disappointment to me, Mr. +Holmes--a grievous disappointment. I have no doubt that I am +myself to blame. People tell me that I have spoiled him. Very +likely I have. When my dear wife died I felt that he was all I +had to love. I could not bear to see the smile fade even for a +moment from his face. I have never denied him a wish. Perhaps it +would have been better for both of us had I been sterner, but I +meant it for the best. + +"It was naturally my intention that he should succeed me in my +business, but he was not of a business turn. He was wild, +wayward, and, to speak the truth, I could not trust him in the +handling of large sums of money. When he was young he became a +member of an aristocratic club, and there, having charming +manners, he was soon the intimate of a number of men with long +purses and expensive habits. He learned to play heavily at cards +and to squander money on the turf, until he had again and again +to come to me and implore me to give him an advance upon his +allowance, that he might settle his debts of honour. He tried +more than once to break away from the dangerous company which he +was keeping, but each time the influence of his friend, Sir +George Burnwell, was enough to draw him back again. + +"And, indeed, I could not wonder that such a man as Sir George +Burnwell should gain an influence over him, for he has frequently +brought him to my house, and I have found myself that I could +hardly resist the fascination of his manner. He is older than +Arthur, a man of the world to his finger-tips, one who had been +everywhere, seen everything, a brilliant talker, and a man of +great personal beauty. Yet when I think of him in cold blood, far +away from the glamour of his presence, I am convinced from his +cynical speech and the look which I have caught in his eyes that +he is one who should be deeply distrusted. So I think, and so, +too, thinks my little Mary, who has a woman's quick insight into +character. + +"And now there is only she to be described. She is my niece; but +when my brother died five years ago and left her alone in the +world I adopted her, and have looked upon her ever since as my +daughter. She is a sunbeam in my house--sweet, loving, beautiful, +a wonderful manager and housekeeper, yet as tender and quiet and +gentle as a woman could be. She is my right hand. I do not know +what I could do without her. In only one matter has she ever gone +against my wishes. Twice my boy has asked her to marry him, for +he loves her devotedly, but each time she has refused him. I +think that if anyone could have drawn him into the right path it +would have been she, and that his marriage might have changed his +whole life; but now, alas! it is too late--forever too late! + +"Now, Mr. Holmes, you know the people who live under my roof, and +I shall continue with my miserable story. + +"When we were taking coffee in the drawing-room that night after +dinner, I told Arthur and Mary my experience, and of the precious +treasure which we had under our roof, suppressing only the name +of my client. Lucy Parr, who had brought in the coffee, had, I am +sure, left the room; but I cannot swear that the door was closed. +Mary and Arthur were much interested and wished to see the famous +coronet, but I thought it better not to disturb it. + +"'Where have you put it?' asked Arthur. + +"'In my own bureau.' + +"'Well, I hope to goodness the house won't be burgled during the +night.' said he. + +"'It is locked up,' I answered. + +"'Oh, any old key will fit that bureau. When I was a youngster I +have opened it myself with the key of the box-room cupboard.' + +"He often had a wild way of talking, so that I thought little of +what he said. He followed me to my room, however, that night with +a very grave face. + +"'Look here, dad,' said he with his eyes cast down, 'can you let +me have 200 pounds?' + +"'No, I cannot!' I answered sharply. 'I have been far too +generous with you in money matters.' + +"'You have been very kind,' said he, 'but I must have this money, +or else I can never show my face inside the club again.' + +"'And a very good thing, too!' I cried. + +"'Yes, but you would not have me leave it a dishonoured man,' +said he. 'I could not bear the disgrace. I must raise the money +in some way, and if you will not let me have it, then I must try +other means.' + +"I was very angry, for this was the third demand during the +month. 'You shall not have a farthing from me,' I cried, on which +he bowed and left the room without another word. + +"When he was gone I unlocked my bureau, made sure that my +treasure was safe, and locked it again. Then I started to go +round the house to see that all was secure--a duty which I +usually leave to Mary but which I thought it well to perform +myself that night. As I came down the stairs I saw Mary herself +at the side window of the hall, which she closed and fastened as +I approached. + +"'Tell me, dad,' said she, looking, I thought, a little +disturbed, 'did you give Lucy, the maid, leave to go out +to-night?' + +"'Certainly not.' + +"'She came in just now by the back door. I have no doubt that she +has only been to the side gate to see someone, but I think that +it is hardly safe and should be stopped.' + +"'You must speak to her in the morning, or I will if you prefer +it. Are you sure that everything is fastened?' + +"'Quite sure, dad.' + +"'Then, good-night.' I kissed her and went up to my bedroom +again, where I was soon asleep. + +"I am endeavouring to tell you everything, Mr. Holmes, which may +have any bearing upon the case, but I beg that you will question +me upon any point which I do not make clear." + +"On the contrary, your statement is singularly lucid." + +"I come to a part of my story now in which I should wish to be +particularly so. I am not a very heavy sleeper, and the anxiety +in my mind tended, no doubt, to make me even less so than usual. +About two in the morning, then, I was awakened by some sound in +the house. It had ceased ere I was wide awake, but it had left an +impression behind it as though a window had gently closed +somewhere. I lay listening with all my ears. Suddenly, to my +horror, there was a distinct sound of footsteps moving softly in +the next room. I slipped out of bed, all palpitating with fear, +and peeped round the corner of my dressing-room door. + +"'Arthur!' I screamed, 'you villain! you thief! How dare you +touch that coronet?' + +"The gas was half up, as I had left it, and my unhappy boy, +dressed only in his shirt and trousers, was standing beside the +light, holding the coronet in his hands. He appeared to be +wrenching at it, or bending it with all his strength. At my cry +he dropped it from his grasp and turned as pale as death. I +snatched it up and examined it. One of the gold corners, with +three of the beryls in it, was missing. + +"'You blackguard!' I shouted, beside myself with rage. 'You have +destroyed it! You have dishonoured me forever! Where are the +jewels which you have stolen?' + +"'Stolen!' he cried. + +"'Yes, thief!' I roared, shaking him by the shoulder. + +"'There are none missing. There cannot be any missing,' said he. + +"'There are three missing. And you know where they are. Must I +call you a liar as well as a thief? Did I not see you trying to +tear off another piece?' + +"'You have called me names enough,' said he, 'I will not stand it +any longer. I shall not say another word about this business, +since you have chosen to insult me. I will leave your house in +the morning and make my own way in the world.' + +"'You shall leave it in the hands of the police!' I cried +half-mad with grief and rage. 'I shall have this matter probed to +the bottom.' + +"'You shall learn nothing from me,' said he with a passion such +as I should not have thought was in his nature. 'If you choose to +call the police, let the police find what they can.' + +"By this time the whole house was astir, for I had raised my +voice in my anger. Mary was the first to rush into my room, and, +at the sight of the coronet and of Arthur's face, she read the +whole story and, with a scream, fell down senseless on the +ground. I sent the house-maid for the police and put the +investigation into their hands at once. When the inspector and a +constable entered the house, Arthur, who had stood sullenly with +his arms folded, asked me whether it was my intention to charge +him with theft. I answered that it had ceased to be a private +matter, but had become a public one, since the ruined coronet was +national property. I was determined that the law should have its +way in everything. + +"'At least,' said he, 'you will not have me arrested at once. It +would be to your advantage as well as mine if I might leave the +house for five minutes.' + +"'That you may get away, or perhaps that you may conceal what you +have stolen,' said I. And then, realising the dreadful position +in which I was placed, I implored him to remember that not only +my honour but that of one who was far greater than I was at +stake; and that he threatened to raise a scandal which would +convulse the nation. He might avert it all if he would but tell +me what he had done with the three missing stones. + +"'You may as well face the matter,' said I; 'you have been caught +in the act, and no confession could make your guilt more heinous. +If you but make such reparation as is in your power, by telling +us where the beryls are, all shall be forgiven and forgotten.' + +"'Keep your forgiveness for those who ask for it,' he answered, +turning away from me with a sneer. I saw that he was too hardened +for any words of mine to influence him. There was but one way for +it. I called in the inspector and gave him into custody. A search +was made at once not only of his person but of his room and of +every portion of the house where he could possibly have concealed +the gems; but no trace of them could be found, nor would the +wretched boy open his mouth for all our persuasions and our +threats. This morning he was removed to a cell, and I, after +going through all the police formalities, have hurried round to +you to implore you to use your skill in unravelling the matter. +The police have openly confessed that they can at present make +nothing of it. You may go to any expense which you think +necessary. I have already offered a reward of 1000 pounds. My +God, what shall I do! I have lost my honour, my gems, and my son +in one night. Oh, what shall I do!" + +He put a hand on either side of his head and rocked himself to +and fro, droning to himself like a child whose grief has got +beyond words. + +Sherlock Holmes sat silent for some few minutes, with his brows +knitted and his eyes fixed upon the fire. + +"Do you receive much company?" he asked. + +"None save my partner with his family and an occasional friend of +Arthur's. Sir George Burnwell has been several times lately. No +one else, I think." + +"Do you go out much in society?" + +"Arthur does. Mary and I stay at home. We neither of us care for +it." + +"That is unusual in a young girl." + +"She is of a quiet nature. Besides, she is not so very young. She +is four-and-twenty." + +"This matter, from what you say, seems to have been a shock to +her also." + +"Terrible! She is even more affected than I." + +"You have neither of you any doubt as to your son's guilt?" + +"How can we have when I saw him with my own eyes with the coronet +in his hands." + +"I hardly consider that a conclusive proof. Was the remainder of +the coronet at all injured?" + +"Yes, it was twisted." + +"Do you not think, then, that he might have been trying to +straighten it?" + +"God bless you! You are doing what you can for him and for me. +But it is too heavy a task. What was he doing there at all? If +his purpose were innocent, why did he not say so?" + +"Precisely. And if it were guilty, why did he not invent a lie? +His silence appears to me to cut both ways. There are several +singular points about the case. What did the police think of the +noise which awoke you from your sleep?" + +"They considered that it might be caused by Arthur's closing his +bedroom door." + +"A likely story! As if a man bent on felony would slam his door +so as to wake a household. What did they say, then, of the +disappearance of these gems?" + +"They are still sounding the planking and probing the furniture +in the hope of finding them." + +"Have they thought of looking outside the house?" + +"Yes, they have shown extraordinary energy. The whole garden has +already been minutely examined." + +"Now, my dear sir," said Holmes, "is it not obvious to you now +that this matter really strikes very much deeper than either you +or the police were at first inclined to think? It appeared to you +to be a simple case; to me it seems exceedingly complex. Consider +what is involved by your theory. You suppose that your son came +down from his bed, went, at great risk, to your dressing-room, +opened your bureau, took out your coronet, broke off by main +force a small portion of it, went off to some other place, +concealed three gems out of the thirty-nine, with such skill that +nobody can find them, and then returned with the other thirty-six +into the room in which he exposed himself to the greatest danger +of being discovered. I ask you now, is such a theory tenable?" + +"But what other is there?" cried the banker with a gesture of +despair. "If his motives were innocent, why does he not explain +them?" + +"It is our task to find that out," replied Holmes; "so now, if +you please, Mr. Holder, we will set off for Streatham together, +and devote an hour to glancing a little more closely into +details." + +My friend insisted upon my accompanying them in their expedition, +which I was eager enough to do, for my curiosity and sympathy +were deeply stirred by the story to which we had listened. I +confess that the guilt of the banker's son appeared to me to be +as obvious as it did to his unhappy father, but still I had such +faith in Holmes' judgment that I felt that there must be some +grounds for hope as long as he was dissatisfied with the accepted +explanation. He hardly spoke a word the whole way out to the +southern suburb, but sat with his chin upon his breast and his +hat drawn over his eyes, sunk in the deepest thought. Our client +appeared to have taken fresh heart at the little glimpse of hope +which had been presented to him, and he even broke into a +desultory chat with me over his business affairs. A short railway +journey and a shorter walk brought us to Fairbank, the modest +residence of the great financier. + +Fairbank was a good-sized square house of white stone, standing +back a little from the road. A double carriage-sweep, with a +snow-clad lawn, stretched down in front to two large iron gates +which closed the entrance. On the right side was a small wooden +thicket, which led into a narrow path between two neat hedges +stretching from the road to the kitchen door, and forming the +tradesmen's entrance. On the left ran a lane which led to the +stables, and was not itself within the grounds at all, being a +public, though little used, thoroughfare. Holmes left us standing +at the door and walked slowly all round the house, across the +front, down the tradesmen's path, and so round by the garden +behind into the stable lane. So long was he that Mr. Holder and I +went into the dining-room and waited by the fire until he should +return. We were sitting there in silence when the door opened and +a young lady came in. She was rather above the middle height, +slim, with dark hair and eyes, which seemed the darker against +the absolute pallor of her skin. I do not think that I have ever +seen such deadly paleness in a woman's face. Her lips, too, were +bloodless, but her eyes were flushed with crying. As she swept +silently into the room she impressed me with a greater sense of +grief than the banker had done in the morning, and it was the +more striking in her as she was evidently a woman of strong +character, with immense capacity for self-restraint. Disregarding +my presence, she went straight to her uncle and passed her hand +over his head with a sweet womanly caress. + +"You have given orders that Arthur should be liberated, have you +not, dad?" she asked. + +"No, no, my girl, the matter must be probed to the bottom." + +"But I am so sure that he is innocent. You know what woman's +instincts are. I know that he has done no harm and that you will +be sorry for having acted so harshly." + +"Why is he silent, then, if he is innocent?" + +"Who knows? Perhaps because he was so angry that you should +suspect him." + +"How could I help suspecting him, when I actually saw him with +the coronet in his hand?" + +"Oh, but he had only picked it up to look at it. Oh, do, do take +my word for it that he is innocent. Let the matter drop and say +no more. It is so dreadful to think of our dear Arthur in +prison!" + +"I shall never let it drop until the gems are found--never, Mary! +Your affection for Arthur blinds you as to the awful consequences +to me. Far from hushing the thing up, I have brought a gentleman +down from London to inquire more deeply into it." + +"This gentleman?" she asked, facing round to me. + +"No, his friend. He wished us to leave him alone. He is round in +the stable lane now." + +"The stable lane?" She raised her dark eyebrows. "What can he +hope to find there? Ah! this, I suppose, is he. I trust, sir, +that you will succeed in proving, what I feel sure is the truth, +that my cousin Arthur is innocent of this crime." + +"I fully share your opinion, and I trust, with you, that we may +prove it," returned Holmes, going back to the mat to knock the +snow from his shoes. "I believe I have the honour of addressing +Miss Mary Holder. Might I ask you a question or two?" + +"Pray do, sir, if it may help to clear this horrible affair up." + +"You heard nothing yourself last night?" + +"Nothing, until my uncle here began to speak loudly. I heard +that, and I came down." + +"You shut up the windows and doors the night before. Did you +fasten all the windows?" + +"Yes." + +"Were they all fastened this morning?" + +"Yes." + +"You have a maid who has a sweetheart? I think that you remarked +to your uncle last night that she had been out to see him?" + +"Yes, and she was the girl who waited in the drawing-room, and +who may have heard uncle's remarks about the coronet." + +"I see. You infer that she may have gone out to tell her +sweetheart, and that the two may have planned the robbery." + +"But what is the good of all these vague theories," cried the +banker impatiently, "when I have told you that I saw Arthur with +the coronet in his hands?" + +"Wait a little, Mr. Holder. We must come back to that. About this +girl, Miss Holder. You saw her return by the kitchen door, I +presume?" + +"Yes; when I went to see if the door was fastened for the night I +met her slipping in. I saw the man, too, in the gloom." + +"Do you know him?" + +"Oh, yes! he is the green-grocer who brings our vegetables round. +His name is Francis Prosper." + +"He stood," said Holmes, "to the left of the door--that is to +say, farther up the path than is necessary to reach the door?" + +"Yes, he did." + +"And he is a man with a wooden leg?" + +Something like fear sprang up in the young lady's expressive +black eyes. "Why, you are like a magician," said she. "How do you +know that?" She smiled, but there was no answering smile in +Holmes' thin, eager face. + +"I should be very glad now to go upstairs," said he. "I shall +probably wish to go over the outside of the house again. Perhaps +I had better take a look at the lower windows before I go up." + +He walked swiftly round from one to the other, pausing only at +the large one which looked from the hall onto the stable lane. +This he opened and made a very careful examination of the sill +with his powerful magnifying lens. "Now we shall go upstairs," +said he at last. + +The banker's dressing-room was a plainly furnished little +chamber, with a grey carpet, a large bureau, and a long mirror. +Holmes went to the bureau first and looked hard at the lock. + +"Which key was used to open it?" he asked. + +"That which my son himself indicated--that of the cupboard of the +lumber-room." + +"Have you it here?" + +"That is it on the dressing-table." + +Sherlock Holmes took it up and opened the bureau. + +"It is a noiseless lock," said he. "It is no wonder that it did +not wake you. This case, I presume, contains the coronet. We must +have a look at it." He opened the case, and taking out the diadem +he laid it upon the table. It was a magnificent specimen of the +jeweller's art, and the thirty-six stones were the finest that I +have ever seen. At one side of the coronet was a cracked edge, +where a corner holding three gems had been torn away. + +"Now, Mr. Holder," said Holmes, "here is the corner which +corresponds to that which has been so unfortunately lost. Might I +beg that you will break it off." + +The banker recoiled in horror. "I should not dream of trying," +said he. + +"Then I will." Holmes suddenly bent his strength upon it, but +without result. "I feel it give a little," said he; "but, though +I am exceptionally strong in the fingers, it would take me all my +time to break it. An ordinary man could not do it. Now, what do +you think would happen if I did break it, Mr. Holder? There would +be a noise like a pistol shot. Do you tell me that all this +happened within a few yards of your bed and that you heard +nothing of it?" + +"I do not know what to think. It is all dark to me." + +"But perhaps it may grow lighter as we go. What do you think, +Miss Holder?" + +"I confess that I still share my uncle's perplexity." + +"Your son had no shoes or slippers on when you saw him?" + +"He had nothing on save only his trousers and shirt." + +"Thank you. We have certainly been favoured with extraordinary +luck during this inquiry, and it will be entirely our own fault +if we do not succeed in clearing the matter up. With your +permission, Mr. Holder, I shall now continue my investigations +outside." + +He went alone, at his own request, for he explained that any +unnecessary footmarks might make his task more difficult. For an +hour or more he was at work, returning at last with his feet +heavy with snow and his features as inscrutable as ever. + +"I think that I have seen now all that there is to see, Mr. +Holder," said he; "I can serve you best by returning to my +rooms." + +"But the gems, Mr. Holmes. Where are they?" + +"I cannot tell." + +The banker wrung his hands. "I shall never see them again!" he +cried. "And my son? You give me hopes?" + +"My opinion is in no way altered." + +"Then, for God's sake, what was this dark business which was +acted in my house last night?" + +"If you can call upon me at my Baker Street rooms to-morrow +morning between nine and ten I shall be happy to do what I can to +make it clearer. I understand that you give me carte blanche to +act for you, provided only that I get back the gems, and that you +place no limit on the sum I may draw." + +"I would give my fortune to have them back." + +"Very good. I shall look into the matter between this and then. +Good-bye; it is just possible that I may have to come over here +again before evening." + +It was obvious to me that my companion's mind was now made up +about the case, although what his conclusions were was more than +I could even dimly imagine. Several times during our homeward +journey I endeavoured to sound him upon the point, but he always +glided away to some other topic, until at last I gave it over in +despair. It was not yet three when we found ourselves in our +rooms once more. He hurried to his chamber and was down again in +a few minutes dressed as a common loafer. With his collar turned +up, his shiny, seedy coat, his red cravat, and his worn boots, he +was a perfect sample of the class. + +"I think that this should do," said he, glancing into the glass +above the fireplace. "I only wish that you could come with me, +Watson, but I fear that it won't do. I may be on the trail in +this matter, or I may be following a will-o'-the-wisp, but I +shall soon know which it is. I hope that I may be back in a few +hours." He cut a slice of beef from the joint upon the sideboard, +sandwiched it between two rounds of bread, and thrusting this +rude meal into his pocket he started off upon his expedition. + +I had just finished my tea when he returned, evidently in +excellent spirits, swinging an old elastic-sided boot in his +hand. He chucked it down into a corner and helped himself to a +cup of tea. + +"I only looked in as I passed," said he. "I am going right on." + +"Where to?" + +"Oh, to the other side of the West End. It may be some time +before I get back. Don't wait up for me in case I should be +late." + +"How are you getting on?" + +"Oh, so so. Nothing to complain of. I have been out to Streatham +since I saw you last, but I did not call at the house. It is a +very sweet little problem, and I would not have missed it for a +good deal. However, I must not sit gossiping here, but must get +these disreputable clothes off and return to my highly +respectable self." + +I could see by his manner that he had stronger reasons for +satisfaction than his words alone would imply. His eyes twinkled, +and there was even a touch of colour upon his sallow cheeks. He +hastened upstairs, and a few minutes later I heard the slam of +the hall door, which told me that he was off once more upon his +congenial hunt. + +I waited until midnight, but there was no sign of his return, so +I retired to my room. It was no uncommon thing for him to be away +for days and nights on end when he was hot upon a scent, so that +his lateness caused me no surprise. I do not know at what hour he +came in, but when I came down to breakfast in the morning there +he was with a cup of coffee in one hand and the paper in the +other, as fresh and trim as possible. + +"You will excuse my beginning without you, Watson," said he, "but +you remember that our client has rather an early appointment this +morning." + +"Why, it is after nine now," I answered. "I should not be +surprised if that were he. I thought I heard a ring." + +It was, indeed, our friend the financier. I was shocked by the +change which had come over him, for his face which was naturally +of a broad and massive mould, was now pinched and fallen in, +while his hair seemed to me at least a shade whiter. He entered +with a weariness and lethargy which was even more painful than +his violence of the morning before, and he dropped heavily into +the armchair which I pushed forward for him. + +"I do not know what I have done to be so severely tried," said +he. "Only two days ago I was a happy and prosperous man, without +a care in the world. Now I am left to a lonely and dishonoured +age. One sorrow comes close upon the heels of another. My niece, +Mary, has deserted me." + +"Deserted you?" + +"Yes. Her bed this morning had not been slept in, her room was +empty, and a note for me lay upon the hall table. I had said to +her last night, in sorrow and not in anger, that if she had +married my boy all might have been well with him. Perhaps it was +thoughtless of me to say so. It is to that remark that she refers +in this note: + +"'MY DEAREST UNCLE:--I feel that I have brought trouble upon you, +and that if I had acted differently this terrible misfortune +might never have occurred. I cannot, with this thought in my +mind, ever again be happy under your roof, and I feel that I must +leave you forever. Do not worry about my future, for that is +provided for; and, above all, do not search for me, for it will +be fruitless labour and an ill-service to me. In life or in +death, I am ever your loving,--MARY.' + +"What could she mean by that note, Mr. Holmes? Do you think it +points to suicide?" + +"No, no, nothing of the kind. It is perhaps the best possible +solution. I trust, Mr. Holder, that you are nearing the end of +your troubles." + +"Ha! You say so! You have heard something, Mr. Holmes; you have +learned something! Where are the gems?" + +"You would not think 1000 pounds apiece an excessive sum for +them?" + +"I would pay ten." + +"That would be unnecessary. Three thousand will cover the matter. +And there is a little reward, I fancy. Have you your check-book? +Here is a pen. Better make it out for 4000 pounds." + +With a dazed face the banker made out the required check. Holmes +walked over to his desk, took out a little triangular piece of +gold with three gems in it, and threw it down upon the table. + +With a shriek of joy our client clutched it up. + +"You have it!" he gasped. "I am saved! I am saved!" + +The reaction of joy was as passionate as his grief had been, and +he hugged his recovered gems to his bosom. + +"There is one other thing you owe, Mr. Holder," said Sherlock +Holmes rather sternly. + +"Owe!" He caught up a pen. "Name the sum, and I will pay it." + +"No, the debt is not to me. You owe a very humble apology to that +noble lad, your son, who has carried himself in this matter as I +should be proud to see my own son do, should I ever chance to +have one." + +"Then it was not Arthur who took them?" + +"I told you yesterday, and I repeat to-day, that it was not." + +"You are sure of it! Then let us hurry to him at once to let him +know that the truth is known." + +"He knows it already. When I had cleared it all up I had an +interview with him, and finding that he would not tell me the +story, I told it to him, on which he had to confess that I was +right and to add the very few details which were not yet quite +clear to me. Your news of this morning, however, may open his +lips." + +"For heaven's sake, tell me, then, what is this extraordinary +mystery!" + +"I will do so, and I will show you the steps by which I reached +it. And let me say to you, first, that which it is hardest for me +to say and for you to hear: there has been an understanding +between Sir George Burnwell and your niece Mary. They have now +fled together." + +"My Mary? Impossible!" + +"It is unfortunately more than possible; it is certain. Neither +you nor your son knew the true character of this man when you +admitted him into your family circle. He is one of the most +dangerous men in England--a ruined gambler, an absolutely +desperate villain, a man without heart or conscience. Your niece +knew nothing of such men. When he breathed his vows to her, as he +had done to a hundred before her, she flattered herself that she +alone had touched his heart. The devil knows best what he said, +but at least she became his tool and was in the habit of seeing +him nearly every evening." + +"I cannot, and I will not, believe it!" cried the banker with an +ashen face. + +"I will tell you, then, what occurred in your house last night. +Your niece, when you had, as she thought, gone to your room, +slipped down and talked to her lover through the window which +leads into the stable lane. His footmarks had pressed right +through the snow, so long had he stood there. She told him of the +coronet. His wicked lust for gold kindled at the news, and he +bent her to his will. I have no doubt that she loved you, but +there are women in whom the love of a lover extinguishes all +other loves, and I think that she must have been one. She had +hardly listened to his instructions when she saw you coming +downstairs, on which she closed the window rapidly and told you +about one of the servants' escapade with her wooden-legged lover, +which was all perfectly true. + +"Your boy, Arthur, went to bed after his interview with you but +he slept badly on account of his uneasiness about his club debts. +In the middle of the night he heard a soft tread pass his door, +so he rose and, looking out, was surprised to see his cousin +walking very stealthily along the passage until she disappeared +into your dressing-room. Petrified with astonishment, the lad +slipped on some clothes and waited there in the dark to see what +would come of this strange affair. Presently she emerged from the +room again, and in the light of the passage-lamp your son saw +that she carried the precious coronet in her hands. She passed +down the stairs, and he, thrilling with horror, ran along and +slipped behind the curtain near your door, whence he could see +what passed in the hall beneath. He saw her stealthily open the +window, hand out the coronet to someone in the gloom, and then +closing it once more hurry back to her room, passing quite close +to where he stood hid behind the curtain. + +"As long as she was on the scene he could not take any action +without a horrible exposure of the woman whom he loved. But the +instant that she was gone he realised how crushing a misfortune +this would be for you, and how all-important it was to set it +right. He rushed down, just as he was, in his bare feet, opened +the window, sprang out into the snow, and ran down the lane, +where he could see a dark figure in the moonlight. Sir George +Burnwell tried to get away, but Arthur caught him, and there was +a struggle between them, your lad tugging at one side of the +coronet, and his opponent at the other. In the scuffle, your son +struck Sir George and cut him over the eye. Then something +suddenly snapped, and your son, finding that he had the coronet +in his hands, rushed back, closed the window, ascended to your +room, and had just observed that the coronet had been twisted in +the struggle and was endeavouring to straighten it when you +appeared upon the scene." + +"Is it possible?" gasped the banker. + +"You then roused his anger by calling him names at a moment when +he felt that he had deserved your warmest thanks. He could not +explain the true state of affairs without betraying one who +certainly deserved little enough consideration at his hands. He +took the more chivalrous view, however, and preserved her +secret." + +"And that was why she shrieked and fainted when she saw the +coronet," cried Mr. Holder. "Oh, my God! what a blind fool I have +been! And his asking to be allowed to go out for five minutes! +The dear fellow wanted to see if the missing piece were at the +scene of the struggle. How cruelly I have misjudged him!" + +"When I arrived at the house," continued Holmes, "I at once went +very carefully round it to observe if there were any traces in +the snow which might help me. I knew that none had fallen since +the evening before, and also that there had been a strong frost +to preserve impressions. I passed along the tradesmen's path, but +found it all trampled down and indistinguishable. Just beyond it, +however, at the far side of the kitchen door, a woman had stood +and talked with a man, whose round impressions on one side showed +that he had a wooden leg. I could even tell that they had been +disturbed, for the woman had run back swiftly to the door, as was +shown by the deep toe and light heel marks, while Wooden-leg had +waited a little, and then had gone away. I thought at the time +that this might be the maid and her sweetheart, of whom you had +already spoken to me, and inquiry showed it was so. I passed +round the garden without seeing anything more than random tracks, +which I took to be the police; but when I got into the stable +lane a very long and complex story was written in the snow in +front of me. + +"There was a double line of tracks of a booted man, and a second +double line which I saw with delight belonged to a man with naked +feet. I was at once convinced from what you had told me that the +latter was your son. The first had walked both ways, but the +other had run swiftly, and as his tread was marked in places over +the depression of the boot, it was obvious that he had passed +after the other. I followed them up and found they led to the +hall window, where Boots had worn all the snow away while +waiting. Then I walked to the other end, which was a hundred +yards or more down the lane. I saw where Boots had faced round, +where the snow was cut up as though there had been a struggle, +and, finally, where a few drops of blood had fallen, to show me +that I was not mistaken. Boots had then run down the lane, and +another little smudge of blood showed that it was he who had been +hurt. When he came to the highroad at the other end, I found that +the pavement had been cleared, so there was an end to that clue. + +"On entering the house, however, I examined, as you remember, the +sill and framework of the hall window with my lens, and I could +at once see that someone had passed out. I could distinguish the +outline of an instep where the wet foot had been placed in coming +in. I was then beginning to be able to form an opinion as to what +had occurred. A man had waited outside the window; someone had +brought the gems; the deed had been overseen by your son; he had +pursued the thief; had struggled with him; they had each tugged +at the coronet, their united strength causing injuries which +neither alone could have effected. He had returned with the +prize, but had left a fragment in the grasp of his opponent. So +far I was clear. The question now was, who was the man and who +was it brought him the coronet? + +"It is an old maxim of mine that when you have excluded the +impossible, whatever remains, however improbable, must be the +truth. Now, I knew that it was not you who had brought it down, +so there only remained your niece and the maids. But if it were +the maids, why should your son allow himself to be accused in +their place? There could be no possible reason. As he loved his +cousin, however, there was an excellent explanation why he should +retain her secret--the more so as the secret was a disgraceful +one. When I remembered that you had seen her at that window, and +how she had fainted on seeing the coronet again, my conjecture +became a certainty. + +"And who could it be who was her confederate? A lover evidently, +for who else could outweigh the love and gratitude which she must +feel to you? I knew that you went out little, and that your +circle of friends was a very limited one. But among them was Sir +George Burnwell. I had heard of him before as being a man of evil +reputation among women. It must have been he who wore those boots +and retained the missing gems. Even though he knew that Arthur +had discovered him, he might still flatter himself that he was +safe, for the lad could not say a word without compromising his +own family. + +"Well, your own good sense will suggest what measures I took +next. I went in the shape of a loafer to Sir George's house, +managed to pick up an acquaintance with his valet, learned that +his master had cut his head the night before, and, finally, at +the expense of six shillings, made all sure by buying a pair of +his cast-off shoes. With these I journeyed down to Streatham and +saw that they exactly fitted the tracks." + +"I saw an ill-dressed vagabond in the lane yesterday evening," +said Mr. Holder. + +"Precisely. It was I. I found that I had my man, so I came home +and changed my clothes. It was a delicate part which I had to +play then, for I saw that a prosecution must be avoided to avert +scandal, and I knew that so astute a villain would see that our +hands were tied in the matter. I went and saw him. At first, of +course, he denied everything. But when I gave him every +particular that had occurred, he tried to bluster and took down a +life-preserver from the wall. I knew my man, however, and I +clapped a pistol to his head before he could strike. Then he +became a little more reasonable. I told him that we would give +him a price for the stones he held--1000 pounds apiece. That +brought out the first signs of grief that he had shown. 'Why, +dash it all!' said he, 'I've let them go at six hundred for the +three!' I soon managed to get the address of the receiver who had +them, on promising him that there would be no prosecution. Off I +set to him, and after much chaffering I got our stones at 1000 +pounds apiece. Then I looked in upon your son, told him that all +was right, and eventually got to my bed about two o'clock, after +what I may call a really hard day's work." + +"A day which has saved England from a great public scandal," said +the banker, rising. "Sir, I cannot find words to thank you, but +you shall not find me ungrateful for what you have done. Your +skill has indeed exceeded all that I have heard of it. And now I +must fly to my dear boy to apologise to him for the wrong which I +have done him. As to what you tell me of poor Mary, it goes to my +very heart. Not even your skill can inform me where she is now." + +"I think that we may safely say," returned Holmes, "that she is +wherever Sir George Burnwell is. It is equally certain, too, that +whatever her sins are, they will soon receive a more than +sufficient punishment." + + + +XII. THE ADVENTURE OF THE COPPER BEECHES + +"To the man who loves art for its own sake," remarked Sherlock +Holmes, tossing aside the advertisement sheet of the Daily +Telegraph, "it is frequently in its least important and lowliest +manifestations that the keenest pleasure is to be derived. It is +pleasant to me to observe, Watson, that you have so far grasped +this truth that in these little records of our cases which you +have been good enough to draw up, and, I am bound to say, +occasionally to embellish, you have given prominence not so much +to the many causes célèbres and sensational trials in which I +have figured but rather to those incidents which may have been +trivial in themselves, but which have given room for those +faculties of deduction and of logical synthesis which I have made +my special province." + +"And yet," said I, smiling, "I cannot quite hold myself absolved +from the charge of sensationalism which has been urged against my +records." + +"You have erred, perhaps," he observed, taking up a glowing +cinder with the tongs and lighting with it the long cherry-wood +pipe which was wont to replace his clay when he was in a +disputatious rather than a meditative mood--"you have erred +perhaps in attempting to put colour and life into each of your +statements instead of confining yourself to the task of placing +upon record that severe reasoning from cause to effect which is +really the only notable feature about the thing." + +"It seems to me that I have done you full justice in the matter," +I remarked with some coldness, for I was repelled by the egotism +which I had more than once observed to be a strong factor in my +friend's singular character. + +"No, it is not selfishness or conceit," said he, answering, as +was his wont, my thoughts rather than my words. "If I claim full +justice for my art, it is because it is an impersonal thing--a +thing beyond myself. Crime is common. Logic is rare. Therefore it +is upon the logic rather than upon the crime that you should +dwell. You have degraded what should have been a course of +lectures into a series of tales." + +It was a cold morning of the early spring, and we sat after +breakfast on either side of a cheery fire in the old room at +Baker Street. A thick fog rolled down between the lines of +dun-coloured houses, and the opposing windows loomed like dark, +shapeless blurs through the heavy yellow wreaths. Our gas was lit +and shone on the white cloth and glimmer of china and metal, for +the table had not been cleared yet. Sherlock Holmes had been +silent all the morning, dipping continuously into the +advertisement columns of a succession of papers until at last, +having apparently given up his search, he had emerged in no very +sweet temper to lecture me upon my literary shortcomings. + +"At the same time," he remarked after a pause, during which he +had sat puffing at his long pipe and gazing down into the fire, +"you can hardly be open to a charge of sensationalism, for out of +these cases which you have been so kind as to interest yourself +in, a fair proportion do not treat of crime, in its legal sense, +at all. The small matter in which I endeavoured to help the King +of Bohemia, the singular experience of Miss Mary Sutherland, the +problem connected with the man with the twisted lip, and the +incident of the noble bachelor, were all matters which are +outside the pale of the law. But in avoiding the sensational, I +fear that you may have bordered on the trivial." + +"The end may have been so," I answered, "but the methods I hold +to have been novel and of interest." + +"Pshaw, my dear fellow, what do the public, the great unobservant +public, who could hardly tell a weaver by his tooth or a +compositor by his left thumb, care about the finer shades of +analysis and deduction! But, indeed, if you are trivial, I cannot +blame you, for the days of the great cases are past. Man, or at +least criminal man, has lost all enterprise and originality. As +to my own little practice, it seems to be degenerating into an +agency for recovering lost lead pencils and giving advice to +young ladies from boarding-schools. I think that I have touched +bottom at last, however. This note I had this morning marks my +zero-point, I fancy. Read it!" He tossed a crumpled letter across +to me. + +It was dated from Montague Place upon the preceding evening, and +ran thus: + +"DEAR MR. HOLMES:--I am very anxious to consult you as to whether +I should or should not accept a situation which has been offered +to me as governess. I shall call at half-past ten to-morrow if I +do not inconvenience you. Yours faithfully, + "VIOLET HUNTER." + +"Do you know the young lady?" I asked. + +"Not I." + +"It is half-past ten now." + +"Yes, and I have no doubt that is her ring." + +"It may turn out to be of more interest than you think. You +remember that the affair of the blue carbuncle, which appeared to +be a mere whim at first, developed into a serious investigation. +It may be so in this case, also." + +"Well, let us hope so. But our doubts will very soon be solved, +for here, unless I am much mistaken, is the person in question." + +As he spoke the door opened and a young lady entered the room. +She was plainly but neatly dressed, with a bright, quick face, +freckled like a plover's egg, and with the brisk manner of a +woman who has had her own way to make in the world. + +"You will excuse my troubling you, I am sure," said she, as my +companion rose to greet her, "but I have had a very strange +experience, and as I have no parents or relations of any sort +from whom I could ask advice, I thought that perhaps you would be +kind enough to tell me what I should do." + +"Pray take a seat, Miss Hunter. I shall be happy to do anything +that I can to serve you." + +I could see that Holmes was favourably impressed by the manner +and speech of his new client. He looked her over in his searching +fashion, and then composed himself, with his lids drooping and +his finger-tips together, to listen to her story. + +"I have been a governess for five years," said she, "in the +family of Colonel Spence Munro, but two months ago the colonel +received an appointment at Halifax, in Nova Scotia, and took his +children over to America with him, so that I found myself without +a situation. I advertised, and I answered advertisements, but +without success. At last the little money which I had saved began +to run short, and I was at my wit's end as to what I should do. + +"There is a well-known agency for governesses in the West End +called Westaway's, and there I used to call about once a week in +order to see whether anything had turned up which might suit me. +Westaway was the name of the founder of the business, but it is +really managed by Miss Stoper. She sits in her own little office, +and the ladies who are seeking employment wait in an anteroom, +and are then shown in one by one, when she consults her ledgers +and sees whether she has anything which would suit them. + +"Well, when I called last week I was shown into the little office +as usual, but I found that Miss Stoper was not alone. A +prodigiously stout man with a very smiling face and a great heavy +chin which rolled down in fold upon fold over his throat sat at +her elbow with a pair of glasses on his nose, looking very +earnestly at the ladies who entered. As I came in he gave quite a +jump in his chair and turned quickly to Miss Stoper. + +"'That will do,' said he; 'I could not ask for anything better. +Capital! capital!' He seemed quite enthusiastic and rubbed his +hands together in the most genial fashion. He was such a +comfortable-looking man that it was quite a pleasure to look at +him. + +"'You are looking for a situation, miss?' he asked. + +"'Yes, sir.' + +"'As governess?' + +"'Yes, sir.' + +"'And what salary do you ask?' + +"'I had 4 pounds a month in my last place with Colonel Spence +Munro.' + +"'Oh, tut, tut! sweating--rank sweating!' he cried, throwing his +fat hands out into the air like a man who is in a boiling +passion. 'How could anyone offer so pitiful a sum to a lady with +such attractions and accomplishments?' + +"'My accomplishments, sir, may be less than you imagine,' said I. +'A little French, a little German, music, and drawing--' + +"'Tut, tut!' he cried. 'This is all quite beside the question. +The point is, have you or have you not the bearing and deportment +of a lady? There it is in a nutshell. If you have not, you are +not fitted for the rearing of a child who may some day play a +considerable part in the history of the country. But if you have +why, then, how could any gentleman ask you to condescend to +accept anything under the three figures? Your salary with me, +madam, would commence at 100 pounds a year.' + +"You may imagine, Mr. Holmes, that to me, destitute as I was, +such an offer seemed almost too good to be true. The gentleman, +however, seeing perhaps the look of incredulity upon my face, +opened a pocket-book and took out a note. + +"'It is also my custom,' said he, smiling in the most pleasant +fashion until his eyes were just two little shining slits amid +the white creases of his face, 'to advance to my young ladies +half their salary beforehand, so that they may meet any little +expenses of their journey and their wardrobe.' + +"It seemed to me that I had never met so fascinating and so +thoughtful a man. As I was already in debt to my tradesmen, the +advance was a great convenience, and yet there was something +unnatural about the whole transaction which made me wish to know +a little more before I quite committed myself. + +"'May I ask where you live, sir?' said I. + +"'Hampshire. Charming rural place. The Copper Beeches, five miles +on the far side of Winchester. It is the most lovely country, my +dear young lady, and the dearest old country-house.' + +"'And my duties, sir? I should be glad to know what they would +be.' + +"'One child--one dear little romper just six years old. Oh, if +you could see him killing cockroaches with a slipper! Smack! +smack! smack! Three gone before you could wink!' He leaned back +in his chair and laughed his eyes into his head again. + +"I was a little startled at the nature of the child's amusement, +but the father's laughter made me think that perhaps he was +joking. + +"'My sole duties, then,' I asked, 'are to take charge of a single +child?' + +"'No, no, not the sole, not the sole, my dear young lady,' he +cried. 'Your duty would be, as I am sure your good sense would +suggest, to obey any little commands my wife might give, provided +always that they were such commands as a lady might with +propriety obey. You see no difficulty, heh?' + +"'I should be happy to make myself useful.' + +"'Quite so. In dress now, for example. We are faddy people, you +know--faddy but kind-hearted. If you were asked to wear any dress +which we might give you, you would not object to our little whim. +Heh?' + +"'No,' said I, considerably astonished at his words. + +"'Or to sit here, or sit there, that would not be offensive to +you?' + +"'Oh, no.' + +"'Or to cut your hair quite short before you come to us?' + +"I could hardly believe my ears. As you may observe, Mr. Holmes, +my hair is somewhat luxuriant, and of a rather peculiar tint of +chestnut. It has been considered artistic. I could not dream of +sacrificing it in this offhand fashion. + +"'I am afraid that that is quite impossible,' said I. He had been +watching me eagerly out of his small eyes, and I could see a +shadow pass over his face as I spoke. + +"'I am afraid that it is quite essential,' said he. 'It is a +little fancy of my wife's, and ladies' fancies, you know, madam, +ladies' fancies must be consulted. And so you won't cut your +hair?' + +"'No, sir, I really could not,' I answered firmly. + +"'Ah, very well; then that quite settles the matter. It is a +pity, because in other respects you would really have done very +nicely. In that case, Miss Stoper, I had best inspect a few more +of your young ladies.' + +"The manageress had sat all this while busy with her papers +without a word to either of us, but she glanced at me now with so +much annoyance upon her face that I could not help suspecting +that she had lost a handsome commission through my refusal. + +"'Do you desire your name to be kept upon the books?' she asked. + +"'If you please, Miss Stoper.' + +"'Well, really, it seems rather useless, since you refuse the +most excellent offers in this fashion,' said she sharply. 'You +can hardly expect us to exert ourselves to find another such +opening for you. Good-day to you, Miss Hunter.' She struck a gong +upon the table, and I was shown out by the page. + +"Well, Mr. Holmes, when I got back to my lodgings and found +little enough in the cupboard, and two or three bills upon the +table, I began to ask myself whether I had not done a very +foolish thing. After all, if these people had strange fads and +expected obedience on the most extraordinary matters, they were +at least ready to pay for their eccentricity. Very few +governesses in England are getting 100 pounds a year. Besides, +what use was my hair to me? Many people are improved by wearing +it short and perhaps I should be among the number. Next day I was +inclined to think that I had made a mistake, and by the day after +I was sure of it. I had almost overcome my pride so far as to go +back to the agency and inquire whether the place was still open +when I received this letter from the gentleman himself. I have it +here and I will read it to you: + + "'The Copper Beeches, near Winchester. +"'DEAR MISS HUNTER:--Miss Stoper has very kindly given me your +address, and I write from here to ask you whether you have +reconsidered your decision. My wife is very anxious that you +should come, for she has been much attracted by my description of +you. We are willing to give 30 pounds a quarter, or 120 pounds a +year, so as to recompense you for any little inconvenience which +our fads may cause you. They are not very exacting, after all. My +wife is fond of a particular shade of electric blue and would +like you to wear such a dress indoors in the morning. You need +not, however, go to the expense of purchasing one, as we have one +belonging to my dear daughter Alice (now in Philadelphia), which +would, I should think, fit you very well. Then, as to sitting +here or there, or amusing yourself in any manner indicated, that +need cause you no inconvenience. As regards your hair, it is no +doubt a pity, especially as I could not help remarking its beauty +during our short interview, but I am afraid that I must remain +firm upon this point, and I only hope that the increased salary +may recompense you for the loss. Your duties, as far as the child +is concerned, are very light. Now do try to come, and I shall +meet you with the dog-cart at Winchester. Let me know your train. +Yours faithfully, JEPHRO RUCASTLE.' + +"That is the letter which I have just received, Mr. Holmes, and +my mind is made up that I will accept it. I thought, however, +that before taking the final step I should like to submit the +whole matter to your consideration." + +"Well, Miss Hunter, if your mind is made up, that settles the +question," said Holmes, smiling. + +"But you would not advise me to refuse?" + +"I confess that it is not the situation which I should like to +see a sister of mine apply for." + +"What is the meaning of it all, Mr. Holmes?" + +"Ah, I have no data. I cannot tell. Perhaps you have yourself +formed some opinion?" + +"Well, there seems to me to be only one possible solution. Mr. +Rucastle seemed to be a very kind, good-natured man. Is it not +possible that his wife is a lunatic, that he desires to keep the +matter quiet for fear she should be taken to an asylum, and that +he humours her fancies in every way in order to prevent an +outbreak?" + +"That is a possible solution--in fact, as matters stand, it is +the most probable one. But in any case it does not seem to be a +nice household for a young lady." + +"But the money, Mr. Holmes, the money!" + +"Well, yes, of course the pay is good--too good. That is what +makes me uneasy. Why should they give you 120 pounds a year, when +they could have their pick for 40 pounds? There must be some +strong reason behind." + +"I thought that if I told you the circumstances you would +understand afterwards if I wanted your help. I should feel so +much stronger if I felt that you were at the back of me." + +"Oh, you may carry that feeling away with you. I assure you that +your little problem promises to be the most interesting which has +come my way for some months. There is something distinctly novel +about some of the features. If you should find yourself in doubt +or in danger--" + +"Danger! What danger do you foresee?" + +Holmes shook his head gravely. "It would cease to be a danger if +we could define it," said he. "But at any time, day or night, a +telegram would bring me down to your help." + +"That is enough." She rose briskly from her chair with the +anxiety all swept from her face. "I shall go down to Hampshire +quite easy in my mind now. I shall write to Mr. Rucastle at once, +sacrifice my poor hair to-night, and start for Winchester +to-morrow." With a few grateful words to Holmes she bade us both +good-night and bustled off upon her way. + +"At least," said I as we heard her quick, firm steps descending +the stairs, "she seems to be a young lady who is very well able +to take care of herself." + +"And she would need to be," said Holmes gravely. "I am much +mistaken if we do not hear from her before many days are past." + +It was not very long before my friend's prediction was fulfilled. +A fortnight went by, during which I frequently found my thoughts +turning in her direction and wondering what strange side-alley of +human experience this lonely woman had strayed into. The unusual +salary, the curious conditions, the light duties, all pointed to +something abnormal, though whether a fad or a plot, or whether +the man were a philanthropist or a villain, it was quite beyond +my powers to determine. As to Holmes, I observed that he sat +frequently for half an hour on end, with knitted brows and an +abstracted air, but he swept the matter away with a wave of his +hand when I mentioned it. "Data! data! data!" he cried +impatiently. "I can't make bricks without clay." And yet he would +always wind up by muttering that no sister of his should ever +have accepted such a situation. + +The telegram which we eventually received came late one night +just as I was thinking of turning in and Holmes was settling down +to one of those all-night chemical researches which he frequently +indulged in, when I would leave him stooping over a retort and a +test-tube at night and find him in the same position when I came +down to breakfast in the morning. He opened the yellow envelope, +and then, glancing at the message, threw it across to me. + +"Just look up the trains in Bradshaw," said he, and turned back +to his chemical studies. + +The summons was a brief and urgent one. + +"Please be at the Black Swan Hotel at Winchester at midday +to-morrow," it said. "Do come! I am at my wit's end. HUNTER." + +"Will you come with me?" asked Holmes, glancing up. + +"I should wish to." + +"Just look it up, then." + +"There is a train at half-past nine," said I, glancing over my +Bradshaw. "It is due at Winchester at 11:30." + +"That will do very nicely. Then perhaps I had better postpone my +analysis of the acetones, as we may need to be at our best in the +morning." + +By eleven o'clock the next day we were well upon our way to the +old English capital. Holmes had been buried in the morning papers +all the way down, but after we had passed the Hampshire border he +threw them down and began to admire the scenery. It was an ideal +spring day, a light blue sky, flecked with little fleecy white +clouds drifting across from west to east. The sun was shining +very brightly, and yet there was an exhilarating nip in the air, +which set an edge to a man's energy. All over the countryside, +away to the rolling hills around Aldershot, the little red and +grey roofs of the farm-steadings peeped out from amid the light +green of the new foliage. + +"Are they not fresh and beautiful?" I cried with all the +enthusiasm of a man fresh from the fogs of Baker Street. + +But Holmes shook his head gravely. + +"Do you know, Watson," said he, "that it is one of the curses of +a mind with a turn like mine that I must look at everything with +reference to my own special subject. You look at these scattered +houses, and you are impressed by their beauty. I look at them, +and the only thought which comes to me is a feeling of their +isolation and of the impunity with which crime may be committed +there." + +"Good heavens!" I cried. "Who would associate crime with these +dear old homesteads?" + +"They always fill me with a certain horror. It is my belief, +Watson, founded upon my experience, that the lowest and vilest +alleys in London do not present a more dreadful record of sin +than does the smiling and beautiful countryside." + +"You horrify me!" + +"But the reason is very obvious. The pressure of public opinion +can do in the town what the law cannot accomplish. There is no +lane so vile that the scream of a tortured child, or the thud of +a drunkard's blow, does not beget sympathy and indignation among +the neighbours, and then the whole machinery of justice is ever +so close that a word of complaint can set it going, and there is +but a step between the crime and the dock. But look at these +lonely houses, each in its own fields, filled for the most part +with poor ignorant folk who know little of the law. Think of the +deeds of hellish cruelty, the hidden wickedness which may go on, +year in, year out, in such places, and none the wiser. Had this +lady who appeals to us for help gone to live in Winchester, I +should never have had a fear for her. It is the five miles of +country which makes the danger. Still, it is clear that she is +not personally threatened." + +"No. If she can come to Winchester to meet us she can get away." + +"Quite so. She has her freedom." + +"What CAN be the matter, then? Can you suggest no explanation?" + +"I have devised seven separate explanations, each of which would +cover the facts as far as we know them. But which of these is +correct can only be determined by the fresh information which we +shall no doubt find waiting for us. Well, there is the tower of +the cathedral, and we shall soon learn all that Miss Hunter has +to tell." + +The Black Swan is an inn of repute in the High Street, at no +distance from the station, and there we found the young lady +waiting for us. She had engaged a sitting-room, and our lunch +awaited us upon the table. + +"I am so delighted that you have come," she said earnestly. "It +is so very kind of you both; but indeed I do not know what I +should do. Your advice will be altogether invaluable to me." + +"Pray tell us what has happened to you." + +"I will do so, and I must be quick, for I have promised Mr. +Rucastle to be back before three. I got his leave to come into +town this morning, though he little knew for what purpose." + +"Let us have everything in its due order." Holmes thrust his long +thin legs out towards the fire and composed himself to listen. + +"In the first place, I may say that I have met, on the whole, +with no actual ill-treatment from Mr. and Mrs. Rucastle. It is +only fair to them to say that. But I cannot understand them, and +I am not easy in my mind about them." + +"What can you not understand?" + +"Their reasons for their conduct. But you shall have it all just +as it occurred. When I came down, Mr. Rucastle met me here and +drove me in his dog-cart to the Copper Beeches. It is, as he +said, beautifully situated, but it is not beautiful in itself, +for it is a large square block of a house, whitewashed, but all +stained and streaked with damp and bad weather. There are grounds +round it, woods on three sides, and on the fourth a field which +slopes down to the Southampton highroad, which curves past about +a hundred yards from the front door. This ground in front belongs +to the house, but the woods all round are part of Lord +Southerton's preserves. A clump of copper beeches immediately in +front of the hall door has given its name to the place. + +"I was driven over by my employer, who was as amiable as ever, +and was introduced by him that evening to his wife and the child. +There was no truth, Mr. Holmes, in the conjecture which seemed to +us to be probable in your rooms at Baker Street. Mrs. Rucastle is +not mad. I found her to be a silent, pale-faced woman, much +younger than her husband, not more than thirty, I should think, +while he can hardly be less than forty-five. From their +conversation I have gathered that they have been married about +seven years, that he was a widower, and that his only child by +the first wife was the daughter who has gone to Philadelphia. Mr. +Rucastle told me in private that the reason why she had left them +was that she had an unreasoning aversion to her stepmother. As +the daughter could not have been less than twenty, I can quite +imagine that her position must have been uncomfortable with her +father's young wife. + +"Mrs. Rucastle seemed to me to be colourless in mind as well as +in feature. She impressed me neither favourably nor the reverse. +She was a nonentity. It was easy to see that she was passionately +devoted both to her husband and to her little son. Her light grey +eyes wandered continually from one to the other, noting every +little want and forestalling it if possible. He was kind to her +also in his bluff, boisterous fashion, and on the whole they +seemed to be a happy couple. And yet she had some secret sorrow, +this woman. She would often be lost in deep thought, with the +saddest look upon her face. More than once I have surprised her +in tears. I have thought sometimes that it was the disposition of +her child which weighed upon her mind, for I have never met so +utterly spoiled and so ill-natured a little creature. He is small +for his age, with a head which is quite disproportionately large. +His whole life appears to be spent in an alternation between +savage fits of passion and gloomy intervals of sulking. Giving +pain to any creature weaker than himself seems to be his one idea +of amusement, and he shows quite remarkable talent in planning +the capture of mice, little birds, and insects. But I would +rather not talk about the creature, Mr. Holmes, and, indeed, he +has little to do with my story." + +"I am glad of all details," remarked my friend, "whether they +seem to you to be relevant or not." + +"I shall try not to miss anything of importance. The one +unpleasant thing about the house, which struck me at once, was +the appearance and conduct of the servants. There are only two, a +man and his wife. Toller, for that is his name, is a rough, +uncouth man, with grizzled hair and whiskers, and a perpetual +smell of drink. Twice since I have been with them he has been +quite drunk, and yet Mr. Rucastle seemed to take no notice of it. +His wife is a very tall and strong woman with a sour face, as +silent as Mrs. Rucastle and much less amiable. They are a most +unpleasant couple, but fortunately I spend most of my time in the +nursery and my own room, which are next to each other in one +corner of the building. + +"For two days after my arrival at the Copper Beeches my life was +very quiet; on the third, Mrs. Rucastle came down just after +breakfast and whispered something to her husband. + +"'Oh, yes,' said he, turning to me, 'we are very much obliged to +you, Miss Hunter, for falling in with our whims so far as to cut +your hair. I assure you that it has not detracted in the tiniest +iota from your appearance. We shall now see how the electric-blue +dress will become you. You will find it laid out upon the bed in +your room, and if you would be so good as to put it on we should +both be extremely obliged.' + +"The dress which I found waiting for me was of a peculiar shade +of blue. It was of excellent material, a sort of beige, but it +bore unmistakable signs of having been worn before. It could not +have been a better fit if I had been measured for it. Both Mr. +and Mrs. Rucastle expressed a delight at the look of it, which +seemed quite exaggerated in its vehemence. They were waiting for +me in the drawing-room, which is a very large room, stretching +along the entire front of the house, with three long windows +reaching down to the floor. A chair had been placed close to the +central window, with its back turned towards it. In this I was +asked to sit, and then Mr. Rucastle, walking up and down on the +other side of the room, began to tell me a series of the funniest +stories that I have ever listened to. You cannot imagine how +comical he was, and I laughed until I was quite weary. Mrs. +Rucastle, however, who has evidently no sense of humour, never so +much as smiled, but sat with her hands in her lap, and a sad, +anxious look upon her face. After an hour or so, Mr. Rucastle +suddenly remarked that it was time to commence the duties of the +day, and that I might change my dress and go to little Edward in +the nursery. + +"Two days later this same performance was gone through under +exactly similar circumstances. Again I changed my dress, again I +sat in the window, and again I laughed very heartily at the funny +stories of which my employer had an immense répertoire, and which +he told inimitably. Then he handed me a yellow-backed novel, and +moving my chair a little sideways, that my own shadow might not +fall upon the page, he begged me to read aloud to him. I read for +about ten minutes, beginning in the heart of a chapter, and then +suddenly, in the middle of a sentence, he ordered me to cease and +to change my dress. + +"You can easily imagine, Mr. Holmes, how curious I became as to +what the meaning of this extraordinary performance could possibly +be. They were always very careful, I observed, to turn my face +away from the window, so that I became consumed with the desire +to see what was going on behind my back. At first it seemed to be +impossible, but I soon devised a means. My hand-mirror had been +broken, so a happy thought seized me, and I concealed a piece of +the glass in my handkerchief. On the next occasion, in the midst +of my laughter, I put my handkerchief up to my eyes, and was able +with a little management to see all that there was behind me. I +confess that I was disappointed. There was nothing. At least that +was my first impression. At the second glance, however, I +perceived that there was a man standing in the Southampton Road, +a small bearded man in a grey suit, who seemed to be looking in +my direction. The road is an important highway, and there are +usually people there. This man, however, was leaning against the +railings which bordered our field and was looking earnestly up. I +lowered my handkerchief and glanced at Mrs. Rucastle to find her +eyes fixed upon me with a most searching gaze. She said nothing, +but I am convinced that she had divined that I had a mirror in my +hand and had seen what was behind me. She rose at once. + +"'Jephro,' said she, 'there is an impertinent fellow upon the +road there who stares up at Miss Hunter.' + +"'No friend of yours, Miss Hunter?' he asked. + +"'No, I know no one in these parts.' + +"'Dear me! How very impertinent! Kindly turn round and motion to +him to go away.' + +"'Surely it would be better to take no notice.' + +"'No, no, we should have him loitering here always. Kindly turn +round and wave him away like that.' + +"I did as I was told, and at the same instant Mrs. Rucastle drew +down the blind. That was a week ago, and from that time I have +not sat again in the window, nor have I worn the blue dress, nor +seen the man in the road." + +"Pray continue," said Holmes. "Your narrative promises to be a +most interesting one." + +"You will find it rather disconnected, I fear, and there may +prove to be little relation between the different incidents of +which I speak. On the very first day that I was at the Copper +Beeches, Mr. Rucastle took me to a small outhouse which stands +near the kitchen door. As we approached it I heard the sharp +rattling of a chain, and the sound as of a large animal moving +about. + +"'Look in here!' said Mr. Rucastle, showing me a slit between two +planks. 'Is he not a beauty?' + +"I looked through and was conscious of two glowing eyes, and of a +vague figure huddled up in the darkness. + +"'Don't be frightened,' said my employer, laughing at the start +which I had given. 'It's only Carlo, my mastiff. I call him mine, +but really old Toller, my groom, is the only man who can do +anything with him. We feed him once a day, and not too much then, +so that he is always as keen as mustard. Toller lets him loose +every night, and God help the trespasser whom he lays his fangs +upon. For goodness' sake don't you ever on any pretext set your +foot over the threshold at night, for it's as much as your life +is worth.' + +"The warning was no idle one, for two nights later I happened to +look out of my bedroom window about two o'clock in the morning. +It was a beautiful moonlight night, and the lawn in front of the +house was silvered over and almost as bright as day. I was +standing, rapt in the peaceful beauty of the scene, when I was +aware that something was moving under the shadow of the copper +beeches. As it emerged into the moonshine I saw what it was. It +was a giant dog, as large as a calf, tawny tinted, with hanging +jowl, black muzzle, and huge projecting bones. It walked slowly +across the lawn and vanished into the shadow upon the other side. +That dreadful sentinel sent a chill to my heart which I do not +think that any burglar could have done. + +"And now I have a very strange experience to tell you. I had, as +you know, cut off my hair in London, and I had placed it in a +great coil at the bottom of my trunk. One evening, after the +child was in bed, I began to amuse myself by examining the +furniture of my room and by rearranging my own little things. +There was an old chest of drawers in the room, the two upper ones +empty and open, the lower one locked. I had filled the first two +with my linen, and as I had still much to pack away I was +naturally annoyed at not having the use of the third drawer. It +struck me that it might have been fastened by a mere oversight, +so I took out my bunch of keys and tried to open it. The very +first key fitted to perfection, and I drew the drawer open. There +was only one thing in it, but I am sure that you would never +guess what it was. It was my coil of hair. + +"I took it up and examined it. It was of the same peculiar tint, +and the same thickness. But then the impossibility of the thing +obtruded itself upon me. How could my hair have been locked in +the drawer? With trembling hands I undid my trunk, turned out the +contents, and drew from the bottom my own hair. I laid the two +tresses together, and I assure you that they were identical. Was +it not extraordinary? Puzzle as I would, I could make nothing at +all of what it meant. I returned the strange hair to the drawer, +and I said nothing of the matter to the Rucastles as I felt that +I had put myself in the wrong by opening a drawer which they had +locked. + +"I am naturally observant, as you may have remarked, Mr. Holmes, +and I soon had a pretty good plan of the whole house in my head. +There was one wing, however, which appeared not to be inhabited +at all. A door which faced that which led into the quarters of +the Tollers opened into this suite, but it was invariably locked. +One day, however, as I ascended the stair, I met Mr. Rucastle +coming out through this door, his keys in his hand, and a look on +his face which made him a very different person to the round, +jovial man to whom I was accustomed. His cheeks were red, his +brow was all crinkled with anger, and the veins stood out at his +temples with passion. He locked the door and hurried past me +without a word or a look. + +"This aroused my curiosity, so when I went out for a walk in the +grounds with my charge, I strolled round to the side from which I +could see the windows of this part of the house. There were four +of them in a row, three of which were simply dirty, while the +fourth was shuttered up. They were evidently all deserted. As I +strolled up and down, glancing at them occasionally, Mr. Rucastle +came out to me, looking as merry and jovial as ever. + +"'Ah!' said he, 'you must not think me rude if I passed you +without a word, my dear young lady. I was preoccupied with +business matters.' + +"I assured him that I was not offended. 'By the way,' said I, +'you seem to have quite a suite of spare rooms up there, and one +of them has the shutters up.' + +"He looked surprised and, as it seemed to me, a little startled +at my remark. + +"'Photography is one of my hobbies,' said he. 'I have made my +dark room up there. But, dear me! what an observant young lady we +have come upon. Who would have believed it? Who would have ever +believed it?' He spoke in a jesting tone, but there was no jest +in his eyes as he looked at me. I read suspicion there and +annoyance, but no jest. + +"Well, Mr. Holmes, from the moment that I understood that there +was something about that suite of rooms which I was not to know, +I was all on fire to go over them. It was not mere curiosity, +though I have my share of that. It was more a feeling of duty--a +feeling that some good might come from my penetrating to this +place. They talk of woman's instinct; perhaps it was woman's +instinct which gave me that feeling. At any rate, it was there, +and I was keenly on the lookout for any chance to pass the +forbidden door. + +"It was only yesterday that the chance came. I may tell you that, +besides Mr. Rucastle, both Toller and his wife find something to +do in these deserted rooms, and I once saw him carrying a large +black linen bag with him through the door. Recently he has been +drinking hard, and yesterday evening he was very drunk; and when +I came upstairs there was the key in the door. I have no doubt at +all that he had left it there. Mr. and Mrs. Rucastle were both +downstairs, and the child was with them, so that I had an +admirable opportunity. I turned the key gently in the lock, +opened the door, and slipped through. + +"There was a little passage in front of me, unpapered and +uncarpeted, which turned at a right angle at the farther end. +Round this corner were three doors in a line, the first and third +of which were open. They each led into an empty room, dusty and +cheerless, with two windows in the one and one in the other, so +thick with dirt that the evening light glimmered dimly through +them. The centre door was closed, and across the outside of it +had been fastened one of the broad bars of an iron bed, padlocked +at one end to a ring in the wall, and fastened at the other with +stout cord. The door itself was locked as well, and the key was +not there. This barricaded door corresponded clearly with the +shuttered window outside, and yet I could see by the glimmer from +beneath it that the room was not in darkness. Evidently there was +a skylight which let in light from above. As I stood in the +passage gazing at the sinister door and wondering what secret it +might veil, I suddenly heard the sound of steps within the room +and saw a shadow pass backward and forward against the little +slit of dim light which shone out from under the door. A mad, +unreasoning terror rose up in me at the sight, Mr. Holmes. My +overstrung nerves failed me suddenly, and I turned and ran--ran +as though some dreadful hand were behind me clutching at the +skirt of my dress. I rushed down the passage, through the door, +and straight into the arms of Mr. Rucastle, who was waiting +outside. + +"'So,' said he, smiling, 'it was you, then. I thought that it +must be when I saw the door open.' + +"'Oh, I am so frightened!' I panted. + +"'My dear young lady! my dear young lady!'--you cannot think how +caressing and soothing his manner was--'and what has frightened +you, my dear young lady?' + +"But his voice was just a little too coaxing. He overdid it. I +was keenly on my guard against him. + +"'I was foolish enough to go into the empty wing,' I answered. +'But it is so lonely and eerie in this dim light that I was +frightened and ran out again. Oh, it is so dreadfully still in +there!' + +"'Only that?' said he, looking at me keenly. + +"'Why, what did you think?' I asked. + +"'Why do you think that I lock this door?' + +"'I am sure that I do not know.' + +"'It is to keep people out who have no business there. Do you +see?' He was still smiling in the most amiable manner. + +"'I am sure if I had known--' + +"'Well, then, you know now. And if you ever put your foot over +that threshold again'--here in an instant the smile hardened into +a grin of rage, and he glared down at me with the face of a +demon--'I'll throw you to the mastiff.' + +"I was so terrified that I do not know what I did. I suppose that +I must have rushed past him into my room. I remember nothing +until I found myself lying on my bed trembling all over. Then I +thought of you, Mr. Holmes. I could not live there longer without +some advice. I was frightened of the house, of the man, of the +woman, of the servants, even of the child. They were all horrible +to me. If I could only bring you down all would be well. Of +course I might have fled from the house, but my curiosity was +almost as strong as my fears. My mind was soon made up. I would +send you a wire. I put on my hat and cloak, went down to the +office, which is about half a mile from the house, and then +returned, feeling very much easier. A horrible doubt came into my +mind as I approached the door lest the dog might be loose, but I +remembered that Toller had drunk himself into a state of +insensibility that evening, and I knew that he was the only one +in the household who had any influence with the savage creature, +or who would venture to set him free. I slipped in in safety and +lay awake half the night in my joy at the thought of seeing you. +I had no difficulty in getting leave to come into Winchester this +morning, but I must be back before three o'clock, for Mr. and +Mrs. Rucastle are going on a visit, and will be away all the +evening, so that I must look after the child. Now I have told you +all my adventures, Mr. Holmes, and I should be very glad if you +could tell me what it all means, and, above all, what I should +do." + +Holmes and I had listened spellbound to this extraordinary story. +My friend rose now and paced up and down the room, his hands in +his pockets, and an expression of the most profound gravity upon +his face. + +"Is Toller still drunk?" he asked. + +"Yes. I heard his wife tell Mrs. Rucastle that she could do +nothing with him." + +"That is well. And the Rucastles go out to-night?" + +"Yes." + +"Is there a cellar with a good strong lock?" + +"Yes, the wine-cellar." + +"You seem to me to have acted all through this matter like a very +brave and sensible girl, Miss Hunter. Do you think that you could +perform one more feat? I should not ask it of you if I did not +think you a quite exceptional woman." + +"I will try. What is it?" + +"We shall be at the Copper Beeches by seven o'clock, my friend +and I. The Rucastles will be gone by that time, and Toller will, +we hope, be incapable. There only remains Mrs. Toller, who might +give the alarm. If you could send her into the cellar on some +errand, and then turn the key upon her, you would facilitate +matters immensely." + +"I will do it." + +"Excellent! We shall then look thoroughly into the affair. Of +course there is only one feasible explanation. You have been +brought there to personate someone, and the real person is +imprisoned in this chamber. That is obvious. As to who this +prisoner is, I have no doubt that it is the daughter, Miss Alice +Rucastle, if I remember right, who was said to have gone to +America. You were chosen, doubtless, as resembling her in height, +figure, and the colour of your hair. Hers had been cut off, very +possibly in some illness through which she has passed, and so, of +course, yours had to be sacrificed also. By a curious chance you +came upon her tresses. The man in the road was undoubtedly some +friend of hers--possibly her fiancé--and no doubt, as you wore +the girl's dress and were so like her, he was convinced from your +laughter, whenever he saw you, and afterwards from your gesture, +that Miss Rucastle was perfectly happy, and that she no longer +desired his attentions. The dog is let loose at night to prevent +him from endeavouring to communicate with her. So much is fairly +clear. The most serious point in the case is the disposition of +the child." + +"What on earth has that to do with it?" I ejaculated. + +"My dear Watson, you as a medical man are continually gaining +light as to the tendencies of a child by the study of the +parents. Don't you see that the converse is equally valid. I have +frequently gained my first real insight into the character of +parents by studying their children. This child's disposition is +abnormally cruel, merely for cruelty's sake, and whether he +derives this from his smiling father, as I should suspect, or +from his mother, it bodes evil for the poor girl who is in their +power." + +"I am sure that you are right, Mr. Holmes," cried our client. "A +thousand things come back to me which make me certain that you +have hit it. Oh, let us lose not an instant in bringing help to +this poor creature." + +"We must be circumspect, for we are dealing with a very cunning +man. We can do nothing until seven o'clock. At that hour we shall +be with you, and it will not be long before we solve the +mystery." + +We were as good as our word, for it was just seven when we +reached the Copper Beeches, having put up our trap at a wayside +public-house. The group of trees, with their dark leaves shining +like burnished metal in the light of the setting sun, were +sufficient to mark the house even had Miss Hunter not been +standing smiling on the door-step. + +"Have you managed it?" asked Holmes. + +A loud thudding noise came from somewhere downstairs. "That is +Mrs. Toller in the cellar," said she. "Her husband lies snoring +on the kitchen rug. Here are his keys, which are the duplicates +of Mr. Rucastle's." + +"You have done well indeed!" cried Holmes with enthusiasm. "Now +lead the way, and we shall soon see the end of this black +business." + +We passed up the stair, unlocked the door, followed on down a +passage, and found ourselves in front of the barricade which Miss +Hunter had described. Holmes cut the cord and removed the +transverse bar. Then he tried the various keys in the lock, but +without success. No sound came from within, and at the silence +Holmes' face clouded over. + +"I trust that we are not too late," said he. "I think, Miss +Hunter, that we had better go in without you. Now, Watson, put +your shoulder to it, and we shall see whether we cannot make our +way in." + +It was an old rickety door and gave at once before our united +strength. Together we rushed into the room. It was empty. There +was no furniture save a little pallet bed, a small table, and a +basketful of linen. The skylight above was open, and the prisoner +gone. + +"There has been some villainy here," said Holmes; "this beauty +has guessed Miss Hunter's intentions and has carried his victim +off." + +"But how?" + +"Through the skylight. We shall soon see how he managed it." He +swung himself up onto the roof. "Ah, yes," he cried, "here's the +end of a long light ladder against the eaves. That is how he did +it." + +"But it is impossible," said Miss Hunter; "the ladder was not +there when the Rucastles went away." + +"He has come back and done it. I tell you that he is a clever and +dangerous man. I should not be very much surprised if this were +he whose step I hear now upon the stair. I think, Watson, that it +would be as well for you to have your pistol ready." + +The words were hardly out of his mouth before a man appeared at +the door of the room, a very fat and burly man, with a heavy +stick in his hand. Miss Hunter screamed and shrunk against the +wall at the sight of him, but Sherlock Holmes sprang forward and +confronted him. + +"You villain!" said he, "where's your daughter?" + +The fat man cast his eyes round, and then up at the open +skylight. + +"It is for me to ask you that," he shrieked, "you thieves! Spies +and thieves! I have caught you, have I? You are in my power. I'll +serve you!" He turned and clattered down the stairs as hard as he +could go. + +"He's gone for the dog!" cried Miss Hunter. + +"I have my revolver," said I. + +"Better close the front door," cried Holmes, and we all rushed +down the stairs together. We had hardly reached the hall when we +heard the baying of a hound, and then a scream of agony, with a +horrible worrying sound which it was dreadful to listen to. An +elderly man with a red face and shaking limbs came staggering out +at a side door. + +"My God!" he cried. "Someone has loosed the dog. It's not been +fed for two days. Quick, quick, or it'll be too late!" + +Holmes and I rushed out and round the angle of the house, with +Toller hurrying behind us. There was the huge famished brute, its +black muzzle buried in Rucastle's throat, while he writhed and +screamed upon the ground. Running up, I blew its brains out, and +it fell over with its keen white teeth still meeting in the great +creases of his neck. With much labour we separated them and +carried him, living but horribly mangled, into the house. We laid +him upon the drawing-room sofa, and having dispatched the sobered +Toller to bear the news to his wife, I did what I could to +relieve his pain. We were all assembled round him when the door +opened, and a tall, gaunt woman entered the room. + +"Mrs. Toller!" cried Miss Hunter. + +"Yes, miss. Mr. Rucastle let me out when he came back before he +went up to you. Ah, miss, it is a pity you didn't let me know +what you were planning, for I would have told you that your pains +were wasted." + +"Ha!" said Holmes, looking keenly at her. "It is clear that Mrs. +Toller knows more about this matter than anyone else." + +"Yes, sir, I do, and I am ready enough to tell what I know." + +"Then, pray, sit down, and let us hear it for there are several +points on which I must confess that I am still in the dark." + +"I will soon make it clear to you," said she; "and I'd have done +so before now if I could ha' got out from the cellar. If there's +police-court business over this, you'll remember that I was the +one that stood your friend, and that I was Miss Alice's friend +too. + +"She was never happy at home, Miss Alice wasn't, from the time +that her father married again. She was slighted like and had no +say in anything, but it never really became bad for her until +after she met Mr. Fowler at a friend's house. As well as I could +learn, Miss Alice had rights of her own by will, but she was so +quiet and patient, she was, that she never said a word about them +but just left everything in Mr. Rucastle's hands. He knew he was +safe with her; but when there was a chance of a husband coming +forward, who would ask for all that the law would give him, then +her father thought it time to put a stop on it. He wanted her to +sign a paper, so that whether she married or not, he could use +her money. When she wouldn't do it, he kept on worrying her until +she got brain-fever, and for six weeks was at death's door. Then +she got better at last, all worn to a shadow, and with her +beautiful hair cut off; but that didn't make no change in her +young man, and he stuck to her as true as man could be." + +"Ah," said Holmes, "I think that what you have been good enough +to tell us makes the matter fairly clear, and that I can deduce +all that remains. Mr. Rucastle then, I presume, took to this +system of imprisonment?" + +"Yes, sir." + +"And brought Miss Hunter down from London in order to get rid of +the disagreeable persistence of Mr. Fowler." + +"That was it, sir." + +"But Mr. Fowler being a persevering man, as a good seaman should +be, blockaded the house, and having met you succeeded by certain +arguments, metallic or otherwise, in convincing you that your +interests were the same as his." + +"Mr. Fowler was a very kind-spoken, free-handed gentleman," said +Mrs. Toller serenely. + +"And in this way he managed that your good man should have no +want of drink, and that a ladder should be ready at the moment +when your master had gone out." + +"You have it, sir, just as it happened." + +"I am sure we owe you an apology, Mrs. Toller," said Holmes, "for +you have certainly cleared up everything which puzzled us. And +here comes the country surgeon and Mrs. Rucastle, so I think, +Watson, that we had best escort Miss Hunter back to Winchester, +as it seems to me that our locus standi now is rather a +questionable one." + +And thus was solved the mystery of the sinister house with the +copper beeches in front of the door. Mr. Rucastle survived, but +was always a broken man, kept alive solely through the care of +his devoted wife. They still live with their old servants, who +probably know so much of Rucastle's past life that he finds it +difficult to part from them. Mr. Fowler and Miss Rucastle were +married, by special license, in Southampton the day after their +flight, and he is now the holder of a government appointment in +the island of Mauritius. As to Miss Violet Hunter, my friend +Holmes, rather to my disappointment, manifested no further +interest in her when once she had ceased to be the centre of one +of his problems, and she is now the head of a private school at +Walsall, where I believe that she has met with considerable success. + + + + + + + + + +End of the Project Gutenberg EBook of The Adventures of Sherlock Holmes, by +Arthur Conan Doyle + +*** END OF THIS PROJECT GUTENBERG EBOOK THE ADVENTURES OF SHERLOCK HOLMES *** + +***** This file should be named 1661-8.txt or 1661-8.zip ***** +This and all associated files of various formats will be found in: + http://www.gutenberg.org/1/6/6/1661/ + +Produced by an anonymous Project Gutenberg volunteer and Jose Menendez + +Updated editions will replace the previous one--the old editions +will be renamed. + +Creating the works from public domain print editions means that no +one owns a United States copyright in these works, so the Foundation +(and you!) can copy and distribute it in the United States without +permission and without paying copyright royalties. Special rules, +set forth in the General Terms of Use part of this license, apply to +copying and distributing Project Gutenberg-tm electronic works to +protect the PROJECT GUTENBERG-tm concept and trademark. Project +Gutenberg is a registered trademark, and may not be used if you +charge for the eBooks, unless you receive specific permission. If you +do not charge anything for copies of this eBook, complying with the +rules is very easy. You may use this eBook for nearly any purpose +such as creation of derivative works, reports, performances and +research. They may be modified and printed and given away--you may do +practically ANYTHING with public domain eBooks. Redistribution is +subject to the trademark license, especially commercial +redistribution. + + + +*** START: FULL LICENSE *** + +THE FULL PROJECT GUTENBERG LICENSE +PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK + +To protect the Project Gutenberg-tm mission of promoting the free +distribution of electronic works, by using or distributing this work +(or any other work associated in any way with the phrase "Project +Gutenberg"), you agree to comply with all the terms of the Full Project +Gutenberg-tm License (available with this file or online at +http://gutenberg.net/license). + + +Section 1. General Terms of Use and Redistributing Project Gutenberg-tm +electronic works + +1.A. By reading or using any part of this Project Gutenberg-tm +electronic work, you indicate that you have read, understand, agree to +and accept all the terms of this license and intellectual property +(trademark/copyright) agreement. If you do not agree to abide by all +the terms of this agreement, you must cease using and return or destroy +all copies of Project Gutenberg-tm electronic works in your possession. +If you paid a fee for obtaining a copy of or access to a Project +Gutenberg-tm electronic work and you do not agree to be bound by the +terms of this agreement, you may obtain a refund from the person or +entity to whom you paid the fee as set forth in paragraph 1.E.8. + +1.B. "Project Gutenberg" is a registered trademark. It may only be +used on or associated in any way with an electronic work by people who +agree to be bound by the terms of this agreement. There are a few +things that you can do with most Project Gutenberg-tm electronic works +even without complying with the full terms of this agreement. See +paragraph 1.C below. There are a lot of things you can do with Project +Gutenberg-tm electronic works if you follow the terms of this agreement +and help preserve free future access to Project Gutenberg-tm electronic +works. See paragraph 1.E below. + +1.C. The Project Gutenberg Literary Archive Foundation ("the Foundation" +or PGLAF), owns a compilation copyright in the collection of Project +Gutenberg-tm electronic works. Nearly all the individual works in the +collection are in the public domain in the United States. If an +individual work is in the public domain in the United States and you are +located in the United States, we do not claim a right to prevent you from +copying, distributing, performing, displaying or creating derivative +works based on the work as long as all references to Project Gutenberg +are removed. Of course, we hope that you will support the Project +Gutenberg-tm mission of promoting free access to electronic works by +freely sharing Project Gutenberg-tm works in compliance with the terms of +this agreement for keeping the Project Gutenberg-tm name associated with +the work. You can easily comply with the terms of this agreement by +keeping this work in the same format with its attached full Project +Gutenberg-tm License when you share it without charge with others. + +1.D. The copyright laws of the place where you are located also govern +what you can do with this work. Copyright laws in most countries are in +a constant state of change. If you are outside the United States, check +the laws of your country in addition to the terms of this agreement +before downloading, copying, displaying, performing, distributing or +creating derivative works based on this work or any other Project +Gutenberg-tm work. The Foundation makes no representations concerning +the copyright status of any work in any country outside the United +States. + +1.E. Unless you have removed all references to Project Gutenberg: + +1.E.1. The following sentence, with active links to, or other immediate +access to, the full Project Gutenberg-tm License must appear prominently +whenever any copy of a Project Gutenberg-tm work (any work on which the +phrase "Project Gutenberg" appears, or with which the phrase "Project +Gutenberg" is associated) is accessed, displayed, performed, viewed, +copied or distributed: + +This eBook is for the use of anyone anywhere at no cost and with +almost no restrictions whatsoever. You may copy it, give it away or +re-use it under the terms of the Project Gutenberg License included +with this eBook or online at www.gutenberg.net + +1.E.2. If an individual Project Gutenberg-tm electronic work is derived +from the public domain (does not contain a notice indicating that it is +posted with permission of the copyright holder), the work can be copied +and distributed to anyone in the United States without paying any fees +or charges. If you are redistributing or providing access to a work +with the phrase "Project Gutenberg" associated with or appearing on the +work, you must comply either with the requirements of paragraphs 1.E.1 +through 1.E.7 or obtain permission for the use of the work and the +Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or +1.E.9. + +1.E.3. If an individual Project Gutenberg-tm electronic work is posted +with the permission of the copyright holder, your use and distribution +must comply with both paragraphs 1.E.1 through 1.E.7 and any additional +terms imposed by the copyright holder. Additional terms will be linked +to the Project Gutenberg-tm License for all works posted with the +permission of the copyright holder found at the beginning of this work. + +1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm +License terms from this work, or any files containing a part of this +work or any other work associated with Project Gutenberg-tm. + +1.E.5. Do not copy, display, perform, distribute or redistribute this +electronic work, or any part of this electronic work, without +prominently displaying the sentence set forth in paragraph 1.E.1 with +active links or immediate access to the full terms of the Project +Gutenberg-tm License. + +1.E.6. You may convert to and distribute this work in any binary, +compressed, marked up, nonproprietary or proprietary form, including any +word processing or hypertext form. However, if you provide access to or +distribute copies of a Project Gutenberg-tm work in a format other than +"Plain Vanilla ASCII" or other format used in the official version +posted on the official Project Gutenberg-tm web site (www.gutenberg.net), +you must, at no additional cost, fee or expense to the user, provide a +copy, a means of exporting a copy, or a means of obtaining a copy upon +request, of the work in its original "Plain Vanilla ASCII" or other +form. Any alternate format must include the full Project Gutenberg-tm +License as specified in paragraph 1.E.1. + +1.E.7. Do not charge a fee for access to, viewing, displaying, +performing, copying or distributing any Project Gutenberg-tm works +unless you comply with paragraph 1.E.8 or 1.E.9. + +1.E.8. You may charge a reasonable fee for copies of or providing +access to or distributing Project Gutenberg-tm electronic works provided +that + +- You pay a royalty fee of 20% of the gross profits you derive from + the use of Project Gutenberg-tm works calculated using the method + you already use to calculate your applicable taxes. The fee is + owed to the owner of the Project Gutenberg-tm trademark, but he + has agreed to donate royalties under this paragraph to the + Project Gutenberg Literary Archive Foundation. Royalty payments + must be paid within 60 days following each date on which you + prepare (or are legally required to prepare) your periodic tax + returns. Royalty payments should be clearly marked as such and + sent to the Project Gutenberg Literary Archive Foundation at the + address specified in Section 4, "Information about donations to + the Project Gutenberg Literary Archive Foundation." + +- You provide a full refund of any money paid by a user who notifies + you in writing (or by e-mail) within 30 days of receipt that s/he + does not agree to the terms of the full Project Gutenberg-tm + License. You must require such a user to return or + destroy all copies of the works possessed in a physical medium + and discontinue all use of and all access to other copies of + Project Gutenberg-tm works. + +- You provide, in accordance with paragraph 1.F.3, a full refund of any + money paid for a work or a replacement copy, if a defect in the + electronic work is discovered and reported to you within 90 days + of receipt of the work. + +- You comply with all other terms of this agreement for free + distribution of Project Gutenberg-tm works. + +1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm +electronic work or group of works on different terms than are set +forth in this agreement, you must obtain permission in writing from +both the Project Gutenberg Literary Archive Foundation and Michael +Hart, the owner of the Project Gutenberg-tm trademark. Contact the +Foundation as set forth in Section 3 below. + +1.F. + +1.F.1. Project Gutenberg volunteers and employees expend considerable +effort to identify, do copyright research on, transcribe and proofread +public domain works in creating the Project Gutenberg-tm +collection. Despite these efforts, Project Gutenberg-tm electronic +works, and the medium on which they may be stored, may contain +"Defects," such as, but not limited to, incomplete, inaccurate or +corrupt data, transcription errors, a copyright or other intellectual +property infringement, a defective or damaged disk or other medium, a +computer virus, or computer codes that damage or cannot be read by +your equipment. + +1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right +of Replacement or Refund" described in paragraph 1.F.3, the Project +Gutenberg Literary Archive Foundation, the owner of the Project +Gutenberg-tm trademark, and any other party distributing a Project +Gutenberg-tm electronic work under this agreement, disclaim all +liability to you for damages, costs and expenses, including legal +fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT +LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE +PROVIDED IN PARAGRAPH 1.F.3. YOU AGREE THAT THE FOUNDATION, THE +TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE +LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR +INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH +DAMAGE. + +1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a +defect in this electronic work within 90 days of receiving it, you can +receive a refund of the money (if any) you paid for it by sending a +written explanation to the person you received the work from. If you +received the work on a physical medium, you must return the medium with +your written explanation. The person or entity that provided you with +the defective work may elect to provide a replacement copy in lieu of a +refund. If you received the work electronically, the person or entity +providing it to you may choose to give you a second opportunity to +receive the work electronically in lieu of a refund. If the second copy +is also defective, you may demand a refund in writing without further +opportunities to fix the problem. + +1.F.4. Except for the limited right of replacement or refund set forth +in paragraph 1.F.3, this work is provided to you 'AS-IS' WITH NO OTHER +WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE. + +1.F.5. Some states do not allow disclaimers of certain implied +warranties or the exclusion or limitation of certain types of damages. +If any disclaimer or limitation set forth in this agreement violates the +law of the state applicable to this agreement, the agreement shall be +interpreted to make the maximum disclaimer or limitation permitted by +the applicable state law. The invalidity or unenforceability of any +provision of this agreement shall not void the remaining provisions. + +1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the +trademark owner, any agent or employee of the Foundation, anyone +providing copies of Project Gutenberg-tm electronic works in accordance +with this agreement, and any volunteers associated with the production, +promotion and distribution of Project Gutenberg-tm electronic works, +harmless from all liability, costs and expenses, including legal fees, +that arise directly or indirectly from any of the following which you do +or cause to occur: (a) distribution of this or any Project Gutenberg-tm +work, (b) alteration, modification, or additions or deletions to any +Project Gutenberg-tm work, and (c) any Defect you cause. + + +Section 2. Information about the Mission of Project Gutenberg-tm + +Project Gutenberg-tm is synonymous with the free distribution of +electronic works in formats readable by the widest variety of computers +including obsolete, old, middle-aged and new computers. It exists +because of the efforts of hundreds of volunteers and donations from +people in all walks of life. + +Volunteers and financial support to provide volunteers with the +assistance they need are critical to reaching Project Gutenberg-tm's +goals and ensuring that the Project Gutenberg-tm collection will +remain freely available for generations to come. In 2001, the Project +Gutenberg Literary Archive Foundation was created to provide a secure +and permanent future for Project Gutenberg-tm and future generations. +To learn more about the Project Gutenberg Literary Archive Foundation +and how your efforts and donations can help, see Sections 3 and 4 +and the Foundation web page at http://www.pglaf.org. + + +Section 3. Information about the Project Gutenberg Literary Archive +Foundation + +The Project Gutenberg Literary Archive Foundation is a non profit +501(c)(3) educational corporation organized under the laws of the +state of Mississippi and granted tax exempt status by the Internal +Revenue Service. The Foundation's EIN or federal tax identification +number is 64-6221541. Its 501(c)(3) letter is posted at +http://pglaf.org/fundraising. Contributions to the Project Gutenberg +Literary Archive Foundation are tax deductible to the full extent +permitted by U.S. federal laws and your state's laws. + +The Foundation's principal office is located at 4557 Melan Dr. S. +Fairbanks, AK, 99712., but its volunteers and employees are scattered +throughout numerous locations. Its business office is located at +809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email +business@pglaf.org. Email contact links and up to date contact +information can be found at the Foundation's web site and official +page at http://pglaf.org + +For additional contact information: + Dr. Gregory B. Newby + Chief Executive and Director + gbnewby@pglaf.org + + +Section 4. Information about Donations to the Project Gutenberg +Literary Archive Foundation + +Project Gutenberg-tm depends upon and cannot survive without wide +spread public support and donations to carry out its mission of +increasing the number of public domain and licensed works that can be +freely distributed in machine readable form accessible by the widest +array of equipment including outdated equipment. Many small donations +($1 to $5,000) are particularly important to maintaining tax exempt +status with the IRS. + +The Foundation is committed to complying with the laws regulating +charities and charitable donations in all 50 states of the United +States. Compliance requirements are not uniform and it takes a +considerable effort, much paperwork and many fees to meet and keep up +with these requirements. We do not solicit donations in locations +where we have not received written confirmation of compliance. To +SEND DONATIONS or determine the status of compliance for any +particular state visit http://pglaf.org + +While we cannot and do not solicit contributions from states where we +have not met the solicitation requirements, we know of no prohibition +against accepting unsolicited donations from donors in such states who +approach us with offers to donate. + +International donations are gratefully accepted, but we cannot make +any statements concerning tax treatment of donations received from +outside the United States. U.S. laws alone swamp our small staff. + +Please check the Project Gutenberg Web pages for current donation +methods and addresses. Donations are accepted in a number of other +ways including including checks, online payments and credit card +donations. To donate, please visit: http://pglaf.org/donate + + +Section 5. General Information About Project Gutenberg-tm electronic +works. + +Professor Michael S. Hart is the originator of the Project Gutenberg-tm +concept of a library of electronic works that could be freely shared +with anyone. For thirty years, he produced and distributed Project +Gutenberg-tm eBooks with only a loose network of volunteer support. + + +Project Gutenberg-tm eBooks are often created from several printed +editions, all of which are confirmed as Public Domain in the U.S. +unless a copyright notice is included. Thus, we do not necessarily +keep eBooks in compliance with any particular paper edition. + + +Most people start at our Web site which has the main PG search facility: + + http://www.gutenberg.net + +This Web site includes information about Project Gutenberg-tm, +including how to make donations to the Project Gutenberg Literary +Archive Foundation, how to help produce our new eBooks, and how to +subscribe to our email newsletter to hear about new eBooks. diff --git a/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/writer.go b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/writer.go new file mode 100644 index 000000000..2bf84da50 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bkaradzic/go-lz4/writer.go @@ -0,0 +1,190 @@ +/* + * Copyright 2011-2012 Branimir Karadzic. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package lz4 + +import ( + "encoding/binary" + "errors" +) + +const ( + minMatch = 4 + hashLog = 17 + hashTableSize = 1 << hashLog + hashShift = (minMatch * 8) - hashLog + incompressible uint32 = 128 + uninitHash = 0x88888888 + + // MaxInputSize is the largest buffer than can be compressed in a single block + MaxInputSize = 0x7E000000 +) + +var ( + // ErrTooLarge indicates the input buffer was too large + ErrTooLarge = errors.New("input too large") +) + +type encoder struct { + src []byte + dst []byte + hashTable []uint32 + pos uint32 + anchor uint32 + dpos uint32 +} + +// CompressBound returns the maximum length of a lz4 block, given it's uncompressed length +func CompressBound(isize int) int { + if isize > MaxInputSize { + return 0 + } + return isize + ((isize) / 255) + 16 + 4 +} + +func (e *encoder) writeLiterals(length, mlLen, pos uint32) { + + ln := length + + var code byte + if ln > runMask-1 { + code = runMask + } else { + code = byte(ln) + } + + if mlLen > mlMask-1 { + e.dst[e.dpos] = (code << mlBits) + byte(mlMask) + } else { + e.dst[e.dpos] = (code << mlBits) + byte(mlLen) + } + e.dpos++ + + if code == runMask { + ln -= runMask + for ; ln > 254; ln -= 255 { + e.dst[e.dpos] = 255 + e.dpos++ + } + + e.dst[e.dpos] = byte(ln) + e.dpos++ + } + + for ii := uint32(0); ii < length; ii++ { + e.dst[e.dpos+ii] = e.src[pos+ii] + } + + e.dpos += length +} + +// Encode returns the encoded form of src. The returned array may be a +// sub-slice of dst if it was large enough to hold the entire output. +func Encode(dst, src []byte) ([]byte, error) { + + if len(src) >= MaxInputSize { + return nil, ErrTooLarge + } + + if n := CompressBound(len(src)); len(dst) < n { + dst = make([]byte, n) + } + + e := encoder{src: src, dst: dst, hashTable: make([]uint32, hashTableSize)} + + binary.LittleEndian.PutUint32(dst, uint32(len(src))) + e.dpos = 4 + + var ( + step uint32 = 1 + limit = incompressible + ) + + for { + if int(e.pos)+12 >= len(e.src) { + e.writeLiterals(uint32(len(e.src))-e.anchor, 0, e.anchor) + return e.dst[:e.dpos], nil + } + + sequence := uint32(e.src[e.pos+3])<<24 | uint32(e.src[e.pos+2])<<16 | uint32(e.src[e.pos+1])<<8 | uint32(e.src[e.pos+0]) + + hash := (sequence * 2654435761) >> hashShift + ref := e.hashTable[hash] + uninitHash + e.hashTable[hash] = e.pos - uninitHash + + if ((e.pos-ref)>>16) != 0 || uint32(e.src[ref+3])<<24|uint32(e.src[ref+2])<<16|uint32(e.src[ref+1])<<8|uint32(e.src[ref+0]) != sequence { + if e.pos-e.anchor > limit { + limit <<= 1 + step += 1 + (step >> 2) + } + e.pos += step + continue + } + + if step > 1 { + e.hashTable[hash] = ref - uninitHash + e.pos -= step - 1 + step = 1 + continue + } + limit = incompressible + + ln := e.pos - e.anchor + back := e.pos - ref + + anchor := e.anchor + + e.pos += minMatch + ref += minMatch + e.anchor = e.pos + + for int(e.pos) < len(e.src)-5 && e.src[e.pos] == e.src[ref] { + e.pos++ + ref++ + } + + mlLen := e.pos - e.anchor + + e.writeLiterals(ln, mlLen, anchor) + e.dst[e.dpos] = uint8(back) + e.dst[e.dpos+1] = uint8(back >> 8) + e.dpos += 2 + + if mlLen > mlMask-1 { + mlLen -= mlMask + for mlLen > 254 { + mlLen -= 255 + + e.dst[e.dpos] = 255 + e.dpos++ + } + + e.dst[e.dpos] = byte(mlLen) + e.dpos++ + } + + e.anchor = e.pos + } +} diff --git a/Godeps/_workspace/src/github.com/calmh/logger/LICENSE b/Godeps/_workspace/src/github.com/calmh/logger/LICENSE new file mode 100644 index 000000000..fa5b4e205 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/logger/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2013 Jakob Borg + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +- The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/calmh/logger/README.md b/Godeps/_workspace/src/github.com/calmh/logger/README.md new file mode 100644 index 000000000..1abbc9336 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/logger/README.md @@ -0,0 +1,15 @@ +logger +====== + +A small wrapper around `log` to provide log levels. + +Documentation +------------- + +http://godoc.org/github.com/calmh/logger + +License +------- + +MIT + diff --git a/Godeps/_workspace/src/github.com/calmh/logger/logger.go b/Godeps/_workspace/src/github.com/calmh/logger/logger.go new file mode 100644 index 000000000..2afa84772 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/logger/logger.go @@ -0,0 +1,187 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +// Package logger implements a standardized logger with callback functionality +package logger + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "strings" + "sync" +) + +type LogLevel int + +const ( + LevelDebug LogLevel = iota + LevelVerbose + LevelInfo + LevelOK + LevelWarn + LevelFatal + NumLevels +) + +// A MessageHandler is called with the log level and message text. +type MessageHandler func(l LogLevel, msg string) + +type Logger struct { + logger *log.Logger + handlers [NumLevels][]MessageHandler + mut sync.Mutex +} + +// The default logger logs to standard output with a time prefix. +var DefaultLogger = New() + +func New() *Logger { + if os.Getenv("LOGGER_DISCARD") != "" { + // Hack to completely disable logging, for example when running benchmarks. + return &Logger{ + logger: log.New(ioutil.Discard, "", 0), + } + } + + return &Logger{ + logger: log.New(os.Stdout, "", log.Ltime), + } +} + +// AddHandler registers a new MessageHandler to receive messages with the +// specified log level or above. +func (l *Logger) AddHandler(level LogLevel, h MessageHandler) { + l.mut.Lock() + defer l.mut.Unlock() + l.handlers[level] = append(l.handlers[level], h) +} + +// See log.SetFlags +func (l *Logger) SetFlags(flag int) { + l.logger.SetFlags(flag) +} + +// See log.SetPrefix +func (l *Logger) SetPrefix(prefix string) { + l.logger.SetPrefix(prefix) +} + +func (l *Logger) callHandlers(level LogLevel, s string) { + for _, h := range l.handlers[level] { + h(level, strings.TrimSpace(s)) + } +} + +// Debugln logs a line with a DEBUG prefix. +func (l *Logger) Debugln(vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintln(vals...) + l.logger.Output(2, "DEBUG: "+s) + l.callHandlers(LevelDebug, s) +} + +// Debugf logs a formatted line with a DEBUG prefix. +func (l *Logger) Debugf(format string, vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintf(format, vals...) + l.logger.Output(2, "DEBUG: "+s) + l.callHandlers(LevelDebug, s) +} + +// Infoln logs a line with a VERBOSE prefix. +func (l *Logger) Verboseln(vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintln(vals...) + l.logger.Output(2, "VERBOSE: "+s) + l.callHandlers(LevelVerbose, s) +} + +// Infof logs a formatted line with a VERBOSE prefix. +func (l *Logger) Verbosef(format string, vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintf(format, vals...) + l.logger.Output(2, "VERBOSE: "+s) + l.callHandlers(LevelVerbose, s) +} + +// Infoln logs a line with an INFO prefix. +func (l *Logger) Infoln(vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintln(vals...) + l.logger.Output(2, "INFO: "+s) + l.callHandlers(LevelInfo, s) +} + +// Infof logs a formatted line with an INFO prefix. +func (l *Logger) Infof(format string, vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintf(format, vals...) + l.logger.Output(2, "INFO: "+s) + l.callHandlers(LevelInfo, s) +} + +// Okln logs a line with an OK prefix. +func (l *Logger) Okln(vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintln(vals...) + l.logger.Output(2, "OK: "+s) + l.callHandlers(LevelOK, s) +} + +// Okf logs a formatted line with an OK prefix. +func (l *Logger) Okf(format string, vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintf(format, vals...) + l.logger.Output(2, "OK: "+s) + l.callHandlers(LevelOK, s) +} + +// Warnln logs a formatted line with a WARNING prefix. +func (l *Logger) Warnln(vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintln(vals...) + l.logger.Output(2, "WARNING: "+s) + l.callHandlers(LevelWarn, s) +} + +// Warnf logs a formatted line with a WARNING prefix. +func (l *Logger) Warnf(format string, vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintf(format, vals...) + l.logger.Output(2, "WARNING: "+s) + l.callHandlers(LevelWarn, s) +} + +// Fatalln logs a line with a FATAL prefix and exits the process with exit +// code 1. +func (l *Logger) Fatalln(vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintln(vals...) + l.logger.Output(2, "FATAL: "+s) + l.callHandlers(LevelFatal, s) + os.Exit(1) +} + +// Fatalf logs a formatted line with a FATAL prefix and exits the process with +// exit code 1. +func (l *Logger) Fatalf(format string, vals ...interface{}) { + l.mut.Lock() + defer l.mut.Unlock() + s := fmt.Sprintf(format, vals...) + l.logger.Output(2, "FATAL: "+s) + l.callHandlers(LevelFatal, s) + os.Exit(1) +} diff --git a/Godeps/_workspace/src/github.com/calmh/logger/logger_test.go b/Godeps/_workspace/src/github.com/calmh/logger/logger_test.go new file mode 100644 index 000000000..39f25ea28 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/logger/logger_test.go @@ -0,0 +1,58 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +package logger + +import ( + "strings" + "testing" +) + +func TestAPI(t *testing.T) { + l := New() + l.SetFlags(0) + l.SetPrefix("testing") + + debug := 0 + l.AddHandler(LevelDebug, checkFunc(t, LevelDebug, "test 0", &debug)) + info := 0 + l.AddHandler(LevelInfo, checkFunc(t, LevelInfo, "test 1", &info)) + warn := 0 + l.AddHandler(LevelWarn, checkFunc(t, LevelWarn, "test 2", &warn)) + ok := 0 + l.AddHandler(LevelOK, checkFunc(t, LevelOK, "test 3", &ok)) + + l.Debugf("test %d", 0) + l.Debugln("test", 0) + l.Infof("test %d", 1) + l.Infoln("test", 1) + l.Warnf("test %d", 2) + l.Warnln("test", 2) + l.Okf("test %d", 3) + l.Okln("test", 3) + + if debug != 2 { + t.Errorf("Debug handler called %d != 2 times", debug) + } + if info != 2 { + t.Errorf("Info handler called %d != 2 times", info) + } + if warn != 2 { + t.Errorf("Warn handler called %d != 2 times", warn) + } + if ok != 2 { + t.Errorf("Ok handler called %d != 2 times", ok) + } +} + +func checkFunc(t *testing.T, expectl LogLevel, expectmsg string, counter *int) func(LogLevel, string) { + return func(l LogLevel, msg string) { + *counter++ + if l != expectl { + t.Errorf("Incorrect message level %d != %d", l, expectl) + } + if !strings.HasSuffix(msg, expectmsg) { + t.Errorf("%q does not end with %q", msg, expectmsg) + } + } +} diff --git a/Godeps/_workspace/src/github.com/calmh/luhn/LICENSE b/Godeps/_workspace/src/github.com/calmh/luhn/LICENSE new file mode 100644 index 000000000..0e07d0e10 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/luhn/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2014 Jakob Borg + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +- The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/calmh/luhn/luhn.go b/Godeps/_workspace/src/github.com/calmh/luhn/luhn.go new file mode 100644 index 000000000..8bcb0cd11 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/luhn/luhn.go @@ -0,0 +1,70 @@ +// Copyright (C) 2014 Jakob Borg + +// Package luhn generates and validates Luhn mod N check digits. +package luhn + +import ( + "fmt" + "strings" +) + +// An alphabet is a string of N characters, representing the digits of a given +// base N. +type Alphabet string + +var ( + Base32 Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" +) + +// Generate returns a check digit for the string s, which should be composed +// of characters from the Alphabet a. +func (a Alphabet) Generate(s string) (rune, error) { + if err := a.check(); err != nil { + return 0, err + } + + factor := 1 + sum := 0 + n := len(a) + + for i := range s { + codepoint := strings.IndexByte(string(a), s[i]) + if codepoint == -1 { + return 0, fmt.Errorf("Digit %q not valid in alphabet %q", s[i], a) + } + addend := factor * codepoint + if factor == 2 { + factor = 1 + } else { + factor = 2 + } + addend = (addend / n) + (addend % n) + sum += addend + } + remainder := sum % n + checkCodepoint := (n - remainder) % n + return rune(a[checkCodepoint]), nil +} + +// Validate returns true if the last character of the string s is correct, for +// a string s composed of characters in the alphabet a. +func (a Alphabet) Validate(s string) bool { + t := s[:len(s)-1] + c, err := a.Generate(t) + if err != nil { + return false + } + return rune(s[len(s)-1]) == c +} + +// check returns an error if the given alphabet does not consist of unique characters +func (a Alphabet) check() error { + cm := make(map[byte]bool, len(a)) + for i := range a { + if cm[a[i]] { + return fmt.Errorf("Digit %q non-unique in alphabet %q", a[i], a) + } + cm[a[i]] = true + } + return nil +} diff --git a/Godeps/_workspace/src/github.com/calmh/luhn/luhn_test.go b/Godeps/_workspace/src/github.com/calmh/luhn/luhn_test.go new file mode 100644 index 000000000..eaef71c02 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/luhn/luhn_test.go @@ -0,0 +1,59 @@ +// Copyright (C) 2014 Jakob Borg + +package luhn_test + +import ( + "testing" + + "github.com/calmh/luhn" +) + +func TestGenerate(t *testing.T) { + // Base 6 Luhn + a := luhn.Alphabet("abcdef") + c, err := a.Generate("abcdef") + if err != nil { + t.Fatal(err) + } + if c != 'e' { + t.Errorf("Incorrect check digit %c != e", c) + } + + // Base 10 Luhn + a = luhn.Alphabet("0123456789") + c, err = a.Generate("7992739871") + if err != nil { + t.Fatal(err) + } + if c != '3' { + t.Errorf("Incorrect check digit %c != 3", c) + } +} + +func TestInvalidString(t *testing.T) { + a := luhn.Alphabet("ABC") + _, err := a.Generate("7992739871") + t.Log(err) + if err == nil { + t.Error("Unexpected nil error") + } +} + +func TestBadAlphabet(t *testing.T) { + a := luhn.Alphabet("01234566789") + _, err := a.Generate("7992739871") + t.Log(err) + if err == nil { + t.Error("Unexpected nil error") + } +} + +func TestValidate(t *testing.T) { + a := luhn.Alphabet("abcdef") + if !a.Validate("abcdefe") { + t.Errorf("Incorrect validation response for abcdefe") + } + if a.Validate("abcdefd") { + t.Errorf("Incorrect validation response for abcdefd") + } +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/.gitignore b/Godeps/_workspace/src/github.com/calmh/xdr/.gitignore new file mode 100644 index 000000000..2d830686d --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/.gitignore @@ -0,0 +1 @@ +coverage.out diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/.travis.yml b/Godeps/_workspace/src/github.com/calmh/xdr/.travis.yml new file mode 100644 index 000000000..a7c4c07b3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/.travis.yml @@ -0,0 +1,19 @@ +language: go +go: +- tip + +install: +- export PATH=$PATH:$HOME/gopath/bin +- go get code.google.com/p/go.tools/cmd/cover +- go get github.com/mattn/goveralls + +script: +- ./generate.sh +- go test -coverprofile=coverage.out + +after_success: +- goveralls -coverprofile=coverage.out -service=travis-ci -package=calmh/xdr -repotoken="$COVERALLS_TOKEN" + +env: + global: + secure: SmgnrGfp2zLrA44ChRMpjPeujubt9veZ8Fx/OseMWECmacyV5N/TuDhzIbwo6QwV4xB0sBacoPzvxQbJRVjNKsPiSu72UbcQmQ7flN4Tf7nW09tSh1iW8NgrpBCq/3UYLoBu2iPBEBKm93IK0aGNAKs6oEkB0fU27iTVBwiTXOY= diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/LICENSE b/Godeps/_workspace/src/github.com/calmh/xdr/LICENSE new file mode 100644 index 000000000..8fdf7b0ad --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2014 Jakob Borg. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/README.md b/Godeps/_workspace/src/github.com/calmh/xdr/README.md new file mode 100644 index 000000000..dfc2e3f6b --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/README.md @@ -0,0 +1,12 @@ +xdr +=== + +[![Build Status](https://img.shields.io/travis/calmh/xdr.svg?style=flat)](https://travis-ci.org/calmh/xdr) +[![Coverage Status](https://img.shields.io/coveralls/calmh/xdr.svg?style=flat)](https://coveralls.io/r/calmh/xdr?branch=master) +[![API Documentation](http://img.shields.io/badge/api-Godoc-blue.svg?style=flat)](http://godoc.org/github.com/calmh/xdr) +[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://opensource.org/licenses/MIT) + +This is an XDR encoding/decoding library. It uses code generation and +not reflection. It supports the IPDR bastardized XDR format when built +with `-tags ipdr`. + diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/bench_test.go b/Godeps/_workspace/src/github.com/calmh/xdr/bench_test.go new file mode 100644 index 000000000..4ba9b814d --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/bench_test.go @@ -0,0 +1,117 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +package xdr_test + +import ( + "io" + "io/ioutil" + "testing" + + "github.com/calmh/xdr" +) + +type XDRBenchStruct struct { + I1 uint64 + I2 uint32 + I3 uint16 + I4 uint8 + Bs0 []byte // max:128 + Bs1 []byte + S0 string // max:128 + S1 string +} + +var res []byte // no to be optimized away +var s = XDRBenchStruct{ + I1: 42, + I2: 43, + I3: 44, + I4: 45, + Bs0: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, + Bs1: []byte{11, 12, 13, 14, 15, 16, 17, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + S0: "Hello World! String one.", + S1: "Hello World! String two.", +} +var e []byte + +func init() { + e, _ = s.MarshalXDR() +} + +func BenchmarkThisMarshal(b *testing.B) { + for i := 0; i < b.N; i++ { + res, _ = s.MarshalXDR() + } +} + +func BenchmarkThisUnmarshal(b *testing.B) { + var t XDRBenchStruct + for i := 0; i < b.N; i++ { + err := t.UnmarshalXDR(e) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkThisEncode(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := s.EncodeXDR(ioutil.Discard) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkThisEncoder(b *testing.B) { + w := xdr.NewWriter(ioutil.Discard) + for i := 0; i < b.N; i++ { + _, err := s.EncodeXDRInto(w) + if err != nil { + b.Fatal(err) + } + } +} + +type repeatReader struct { + data []byte +} + +func (r *repeatReader) Read(bs []byte) (n int, err error) { + if len(bs) > len(r.data) { + err = io.EOF + } + n = copy(bs, r.data) + r.data = r.data[n:] + return n, err +} + +func (r *repeatReader) Reset(bs []byte) { + r.data = bs +} + +func BenchmarkThisDecode(b *testing.B) { + rr := &repeatReader{e} + var t XDRBenchStruct + for i := 0; i < b.N; i++ { + err := t.DecodeXDR(rr) + if err != nil { + b.Fatal(err) + } + rr.Reset(e) + } +} + +func BenchmarkThisDecoder(b *testing.B) { + rr := &repeatReader{e} + r := xdr.NewReader(rr) + var t XDRBenchStruct + for i := 0; i < b.N; i++ { + err := t.DecodeXDRFrom(r) + if err != nil { + b.Fatal(err) + } + rr.Reset(e) + } +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/bench_xdr_test.go b/Godeps/_workspace/src/github.com/calmh/xdr/bench_xdr_test.go new file mode 100644 index 000000000..f77b04f6c --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/bench_xdr_test.go @@ -0,0 +1,201 @@ +// ************************************************************ +// This file is automatically generated by genxdr. Do not edit. +// ************************************************************ + +package xdr_test + +import ( + "bytes" + "io" + + "github.com/calmh/xdr" +) + +/* + +XDRBenchStruct Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++ I1 (64 bits) + +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| I2 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| 0x0000 | I3 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ uint8 Structure \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Bs0 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Bs0 (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Bs1 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Bs1 (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of S0 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ S0 (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of S1 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ S1 (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct XDRBenchStruct { + unsigned hyper I1; + unsigned int I2; + unsigned int I3; + uint8 I4; + opaque Bs0<128>; + opaque Bs1<>; + string S0<128>; + string S1<>; +} + +*/ + +func (o XDRBenchStruct) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o XDRBenchStruct) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o XDRBenchStruct) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o XDRBenchStruct) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o XDRBenchStruct) EncodeXDRInto(xw *xdr.Writer) (int, error) { + xw.WriteUint64(o.I1) + xw.WriteUint32(o.I2) + xw.WriteUint16(o.I3) + xw.WriteUint8(o.I4) + if l := len(o.Bs0); l > 128 { + return xw.Tot(), xdr.ElementSizeExceeded("Bs0", l, 128) + } + xw.WriteBytes(o.Bs0) + xw.WriteBytes(o.Bs1) + if l := len(o.S0); l > 128 { + return xw.Tot(), xdr.ElementSizeExceeded("S0", l, 128) + } + xw.WriteString(o.S0) + xw.WriteString(o.S1) + return xw.Tot(), xw.Error() +} + +func (o *XDRBenchStruct) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *XDRBenchStruct) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *XDRBenchStruct) DecodeXDRFrom(xr *xdr.Reader) error { + o.I1 = xr.ReadUint64() + o.I2 = xr.ReadUint32() + o.I3 = xr.ReadUint16() + o.I4 = xr.ReadUint8() + o.Bs0 = xr.ReadBytesMax(128) + o.Bs1 = xr.ReadBytes() + o.S0 = xr.ReadStringMax(128) + o.S1 = xr.ReadString() + return xr.Error() +} + +/* + +repeatReader Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of data | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ data (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct repeatReader { + opaque data<>; +} + +*/ + +func (o repeatReader) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o repeatReader) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o repeatReader) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o repeatReader) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o repeatReader) EncodeXDRInto(xw *xdr.Writer) (int, error) { + xw.WriteBytes(o.data) + return xw.Tot(), xw.Error() +} + +func (o *repeatReader) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *repeatReader) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *repeatReader) DecodeXDRFrom(xr *xdr.Reader) error { + o.data = xr.ReadBytes() + return xr.Error() +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go b/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go new file mode 100644 index 000000000..5ebb7abaa --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go @@ -0,0 +1,467 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io" + "log" + "os" + "regexp" + "strconv" + "strings" + "text/template" +) + +type fieldInfo struct { + Name string + IsBasic bool // handled by one the native Read/WriteUint64 etc functions + IsSlice bool // field is a slice of FieldType + FieldType string // original type of field, i.e. "int" + Encoder string // the encoder name, i.e. "Uint64" for Read/WriteUint64 + Convert string // what to convert to when encoding, i.e. "uint64" + Max int // max size for slices and strings +} + +type structInfo struct { + Name string + Fields []fieldInfo +} + +var headerTpl = template.Must(template.New("header").Parse(`// ************************************************************ +// This file is automatically generated by genxdr. Do not edit. +// ************************************************************ + +package {{.Package}} + +import ( + "bytes" + "io" + + "github.com/calmh/xdr" +) +`)) + +var encodeTpl = template.Must(template.New("encoder").Parse(` +func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +}//+n + +func (o {{.TypeName}}) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +}//+n + +func (o {{.TypeName}}) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +}//+n + +func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +}//+n + +func (o {{.TypeName}}) EncodeXDRInto(xw *xdr.Writer) (int, error) { + {{range $fieldInfo := .Fields}} + {{if not $fieldInfo.IsSlice}} + {{if ne $fieldInfo.Convert ""}} + xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}})) + {{else if $fieldInfo.IsBasic}} + {{if ge $fieldInfo.Max 1}} + if l := len(o.{{$fieldInfo.Name}}); l > {{$fieldInfo.Max}} { + return xw.Tot(), xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", l, {{$fieldInfo.Max}}) + } + {{end}} + xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}) + {{else}} + _, err := o.{{$fieldInfo.Name}}.EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + {{end}} + {{else}} + {{if ge $fieldInfo.Max 1}} + if l := len(o.{{$fieldInfo.Name}}); l > {{$fieldInfo.Max}} { + return xw.Tot(), xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", l, {{$fieldInfo.Max}}) + } + {{end}} + xw.WriteUint32(uint32(len(o.{{$fieldInfo.Name}}))) + for i := range o.{{$fieldInfo.Name}} { + {{if ne $fieldInfo.Convert ""}} + xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}[i])) + {{else if $fieldInfo.IsBasic}} + xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}[i]) + {{else}} + _, err := o.{{$fieldInfo.Name}}[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + {{end}} + } + {{end}} + {{end}} + return xw.Tot(), xw.Error() +}//+n + +func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +}//+n + +func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +}//+n + +func (o *{{.TypeName}}) DecodeXDRFrom(xr *xdr.Reader) error { + {{range $fieldInfo := .Fields}} + {{if not $fieldInfo.IsSlice}} + {{if ne $fieldInfo.Convert ""}} + o.{{$fieldInfo.Name}} = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}()) + {{else if $fieldInfo.IsBasic}} + {{if ge $fieldInfo.Max 1}} + o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}Max({{$fieldInfo.Max}}) + {{else}} + o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}() + {{end}} + {{else}} + (&o.{{$fieldInfo.Name}}).DecodeXDRFrom(xr) + {{end}} + {{else}} + _{{$fieldInfo.Name}}Size := int(xr.ReadUint32()) + if _{{$fieldInfo.Name}}Size < 0 { + return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}}) + } + {{if ge $fieldInfo.Max 1}} + if _{{$fieldInfo.Name}}Size > {{$fieldInfo.Max}} { + return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}}) + } + {{end}} + o.{{$fieldInfo.Name}} = make([]{{$fieldInfo.FieldType}}, _{{$fieldInfo.Name}}Size) + for i := range o.{{$fieldInfo.Name}} { + {{if ne $fieldInfo.Convert ""}} + o.{{$fieldInfo.Name}}[i] = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}()) + {{else if $fieldInfo.IsBasic}} + o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}() + {{else}} + (&o.{{$fieldInfo.Name}}[i]).DecodeXDRFrom(xr) + {{end}} + } + {{end}} + {{end}} + return xr.Error() +}`)) + +var maxRe = regexp.MustCompile(`\Wmax:(\d+)`) + +type typeSet struct { + Type string + Encoder string +} + +var xdrEncoders = map[string]typeSet{ + "int8": typeSet{"uint8", "Uint8"}, + "uint8": typeSet{"", "Uint8"}, + "int16": typeSet{"uint16", "Uint16"}, + "uint16": typeSet{"", "Uint16"}, + "int32": typeSet{"uint32", "Uint32"}, + "uint32": typeSet{"", "Uint32"}, + "int64": typeSet{"uint64", "Uint64"}, + "uint64": typeSet{"", "Uint64"}, + "int": typeSet{"uint64", "Uint64"}, + "string": typeSet{"", "String"}, + "[]byte": typeSet{"", "Bytes"}, + "bool": typeSet{"", "Bool"}, +} + +func handleStruct(t *ast.StructType) []fieldInfo { + var fs []fieldInfo + + for _, sf := range t.Fields.List { + if len(sf.Names) == 0 { + // We don't handle anonymous fields + continue + } + + fn := sf.Names[0].Name + var max = 0 + if sf.Comment != nil { + c := sf.Comment.List[0].Text + if m := maxRe.FindStringSubmatch(c); m != nil { + max, _ = strconv.Atoi(m[1]) + } + if strings.Contains(c, "noencode") { + continue + } + } + + var f fieldInfo + switch ft := sf.Type.(type) { + case *ast.Ident: + tn := ft.Name + if enc, ok := xdrEncoders[tn]; ok { + f = fieldInfo{ + Name: fn, + IsBasic: true, + FieldType: tn, + Encoder: enc.Encoder, + Convert: enc.Type, + Max: max, + } + } else { + f = fieldInfo{ + Name: fn, + IsBasic: false, + FieldType: tn, + Max: max, + } + } + + case *ast.ArrayType: + if ft.Len != nil { + // We don't handle arrays + continue + } + + tn := ft.Elt.(*ast.Ident).Name + if enc, ok := xdrEncoders["[]"+tn]; ok { + f = fieldInfo{ + Name: fn, + IsBasic: true, + FieldType: tn, + Encoder: enc.Encoder, + Convert: enc.Type, + Max: max, + } + } else if enc, ok := xdrEncoders[tn]; ok { + f = fieldInfo{ + Name: fn, + IsBasic: true, + IsSlice: true, + FieldType: tn, + Encoder: enc.Encoder, + Convert: enc.Type, + Max: max, + } + } else { + f = fieldInfo{ + Name: fn, + IsSlice: true, + FieldType: tn, + Max: max, + } + } + + case *ast.SelectorExpr: + f = fieldInfo{ + Name: fn, + FieldType: ft.Sel.Name, + Max: max, + } + } + + fs = append(fs, f) + } + + return fs +} + +func generateCode(output io.Writer, s structInfo) { + name := s.Name + fs := s.Fields + + var buf bytes.Buffer + err := encodeTpl.Execute(&buf, map[string]interface{}{"TypeName": name, "Fields": fs}) + if err != nil { + panic(err) + } + + bs := regexp.MustCompile(`(\s*\n)+`).ReplaceAll(buf.Bytes(), []byte("\n")) + bs = bytes.Replace(bs, []byte("//+n"), []byte("\n"), -1) + + bs, err = format.Source(bs) + if err != nil { + panic(err) + } + fmt.Fprintln(output, string(bs)) +} + +func uncamelize(s string) string { + return regexp.MustCompile("[a-z][A-Z]").ReplaceAllStringFunc(s, func(camel string) string { + return camel[:1] + " " + camel[1:] + }) +} + +func generateDiagram(output io.Writer, s structInfo) { + sn := s.Name + fs := s.Fields + + fmt.Fprintln(output, sn+" Structure:") + fmt.Fprintln(output) + fmt.Fprintln(output, " 0 1 2 3") + fmt.Fprintln(output, " 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1") + line := "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+" + fmt.Fprintln(output, line) + + for _, f := range fs { + tn := f.FieldType + name := uncamelize(f.Name) + + if f.IsSlice { + fmt.Fprintf(output, "| %s |\n", center("Number of "+name, 61)) + fmt.Fprintln(output, line) + } + switch tn { + case "bool": + fmt.Fprintf(output, "| %s |V|\n", center(name+" (V=0 or 1)", 59)) + fmt.Fprintln(output, line) + case "int16", "uint16": + fmt.Fprintf(output, "| %s | %s |\n", center("0x0000", 29), center(name, 29)) + fmt.Fprintln(output, line) + case "int32", "uint32": + fmt.Fprintf(output, "| %s |\n", center(name, 61)) + fmt.Fprintln(output, line) + case "int64", "uint64": + fmt.Fprintf(output, "| %-61s |\n", "") + fmt.Fprintf(output, "+ %s +\n", center(name+" (64 bits)", 61)) + fmt.Fprintf(output, "| %-61s |\n", "") + fmt.Fprintln(output, line) + case "string", "byte": // XXX We assume slice of byte! + fmt.Fprintf(output, "| %s |\n", center("Length of "+name, 61)) + fmt.Fprintln(output, line) + fmt.Fprintf(output, "/ %61s /\n", "") + fmt.Fprintf(output, "\\ %s \\\n", center(name+" (variable length)", 61)) + fmt.Fprintf(output, "/ %61s /\n", "") + fmt.Fprintln(output, line) + default: + if f.IsSlice { + tn = "Zero or more " + tn + " Structures" + fmt.Fprintf(output, "/ %s /\n", center("", 61)) + fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61)) + fmt.Fprintf(output, "/ %s /\n", center("", 61)) + } else { + tn = tn + " Structure" + fmt.Fprintf(output, "/ %s /\n", center("", 61)) + fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61)) + fmt.Fprintf(output, "/ %s /\n", center("", 61)) + } + fmt.Fprintln(output, line) + } + } + fmt.Fprintln(output) + fmt.Fprintln(output) +} + +func generateXdr(output io.Writer, s structInfo) { + sn := s.Name + fs := s.Fields + + fmt.Fprintf(output, "struct %s {\n", sn) + + for _, f := range fs { + tn := f.FieldType + fn := f.Name + suf := "" + l := "" + if f.Max > 0 { + l = strconv.Itoa(f.Max) + } + if f.IsSlice { + suf = "<" + l + ">" + } + + switch tn { + case "int16", "int32": + fmt.Fprintf(output, "\tint %s%s;\n", fn, suf) + case "uint16", "uint32": + fmt.Fprintf(output, "\tunsigned int %s%s;\n", fn, suf) + case "int64": + fmt.Fprintf(output, "\thyper %s%s;\n", fn, suf) + case "uint64": + fmt.Fprintf(output, "\tunsigned hyper %s%s;\n", fn, suf) + case "string": + fmt.Fprintf(output, "\tstring %s<%s>;\n", fn, l) + case "byte": + fmt.Fprintf(output, "\topaque %s<%s>;\n", fn, l) + default: + fmt.Fprintf(output, "\t%s %s%s;\n", tn, fn, suf) + } + } + fmt.Fprintln(output, "}") + fmt.Fprintln(output) +} + +func center(s string, w int) string { + w -= len(s) + l := w / 2 + r := l + if l+r < w { + r++ + } + return strings.Repeat(" ", l) + s + strings.Repeat(" ", r) +} + +func inspector(structs *[]structInfo) func(ast.Node) bool { + return func(n ast.Node) bool { + switch n := n.(type) { + case *ast.TypeSpec: + switch t := n.Type.(type) { + case *ast.StructType: + name := n.Name.Name + fs := handleStruct(t) + *structs = append(*structs, structInfo{name, fs}) + } + return false + default: + return true + } + } +} + +func main() { + outputFile := flag.String("o", "", "Output file, blank for stdout") + flag.Parse() + fname := flag.Arg(0) + + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments) + if err != nil { + log.Fatal(err) + } + + var structs []structInfo + i := inspector(&structs) + ast.Inspect(f, i) + + var output io.Writer = os.Stdout + if *outputFile != "" { + fd, err := os.Create(*outputFile) + if err != nil { + log.Fatal(err) + } + output = fd + } + + headerTpl.Execute(output, map[string]string{"Package": f.Name.Name}) + for _, s := range structs { + fmt.Fprintf(output, "\n/*\n\n") + generateDiagram(output, s) + generateXdr(output, s) + fmt.Fprintf(output, "*/\n") + generateCode(output, s) + } +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/debug.go b/Godeps/_workspace/src/github.com/calmh/xdr/debug.go new file mode 100644 index 000000000..be3f092b2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/debug.go @@ -0,0 +1,16 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +package xdr + +import ( + "log" + "os" +) + +var ( + debug = len(os.Getenv("XDRTRACE")) > 0 + dl = log.New(os.Stdout, "xdr: ", log.Lshortfile|log.Ltime|log.Lmicroseconds) +) + +const maxDebugBytes = 32 diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/doc.go b/Godeps/_workspace/src/github.com/calmh/xdr/doc.go new file mode 100644 index 000000000..41b59bc1b --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/doc.go @@ -0,0 +1,5 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +// Package xdr implements an XDR (RFC 4506) encoder/decoder. +package xdr diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/encdec_test.go b/Godeps/_workspace/src/github.com/calmh/xdr/encdec_test.go new file mode 100644 index 000000000..c1ee87038 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/encdec_test.go @@ -0,0 +1,79 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +package xdr_test + +import ( + "bytes" + "math/rand" + "reflect" + "testing" + "testing/quick" + + "github.com/calmh/xdr" +) + +// Contains all supported types +type TestStruct struct { + I int + I8 int8 + UI8 uint8 + I16 int16 + UI16 uint16 + I32 int32 + UI32 uint32 + I64 int64 + UI64 uint64 + BS []byte // max:1024 + S string // max:1024 + C Opaque + SS []string // max:1024 +} + +type Opaque [32]byte + +func (u *Opaque) EncodeXDRInto(w *xdr.Writer) (int, error) { + return w.WriteRaw(u[:]) +} + +func (u *Opaque) DecodeXDRFrom(r *xdr.Reader) (int, error) { + return r.ReadRaw(u[:]) +} + +func (Opaque) Generate(rand *rand.Rand, size int) reflect.Value { + var u Opaque + for i := range u[:] { + u[i] = byte(rand.Int()) + } + return reflect.ValueOf(u) +} + +func TestEncDec(t *testing.T) { + fn := func(t0 TestStruct) bool { + bs, err := t0.MarshalXDR() + if err != nil { + t.Fatal(err) + } + var t1 TestStruct + err = t1.UnmarshalXDR(bs) + if err != nil { + t.Fatal(err) + } + + // Not comparing with DeepEqual since we'll unmarshal nil slices as empty + if t0.I != t1.I || + t0.I16 != t1.I16 || t0.UI16 != t1.UI16 || + t0.I32 != t1.I32 || t0.UI32 != t1.UI32 || + t0.I64 != t1.I64 || t0.UI64 != t1.UI64 || + bytes.Compare(t0.BS, t1.BS) != 0 || + t0.S != t1.S || t0.C != t1.C { + t.Logf("%#v", t0) + t.Logf("%#v", t1) + return false + } + return true + } + if err := quick.Check(fn, nil); err != nil { + t.Error(err) + } +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/encdec_xdr_test.go b/Godeps/_workspace/src/github.com/calmh/xdr/encdec_xdr_test.go new file mode 100644 index 000000000..d9693166f --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/encdec_xdr_test.go @@ -0,0 +1,185 @@ +// ************************************************************ +// This file is automatically generated by genxdr. Do not edit. +// ************************************************************ + +package xdr_test + +import ( + "bytes" + "io" + + "github.com/calmh/xdr" +) + +/* + +TestStruct Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ int Structure \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ int8 Structure \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ uint8 Structure \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| 0x0000 | I16 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| 0x0000 | UI16 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| I32 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| UI32 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++ I64 (64 bits) + +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++ UI64 (64 bits) + +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of BS | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ BS (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of S | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ S (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Opaque Structure \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of SS | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of SS | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ SS (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct TestStruct { + int I; + int8 I8; + uint8 UI8; + int I16; + unsigned int UI16; + int I32; + unsigned int UI32; + hyper I64; + unsigned hyper UI64; + opaque BS<1024>; + string S<1024>; + Opaque C; + string SS<1024>; +} + +*/ + +func (o TestStruct) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o TestStruct) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o TestStruct) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o TestStruct) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o TestStruct) EncodeXDRInto(xw *xdr.Writer) (int, error) { + xw.WriteUint64(uint64(o.I)) + xw.WriteUint8(uint8(o.I8)) + xw.WriteUint8(o.UI8) + xw.WriteUint16(uint16(o.I16)) + xw.WriteUint16(o.UI16) + xw.WriteUint32(uint32(o.I32)) + xw.WriteUint32(o.UI32) + xw.WriteUint64(uint64(o.I64)) + xw.WriteUint64(o.UI64) + if l := len(o.BS); l > 1024 { + return xw.Tot(), xdr.ElementSizeExceeded("BS", l, 1024) + } + xw.WriteBytes(o.BS) + if l := len(o.S); l > 1024 { + return xw.Tot(), xdr.ElementSizeExceeded("S", l, 1024) + } + xw.WriteString(o.S) + _, err := o.C.EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + if l := len(o.SS); l > 1024 { + return xw.Tot(), xdr.ElementSizeExceeded("SS", l, 1024) + } + xw.WriteUint32(uint32(len(o.SS))) + for i := range o.SS { + xw.WriteString(o.SS[i]) + } + return xw.Tot(), xw.Error() +} + +func (o *TestStruct) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *TestStruct) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *TestStruct) DecodeXDRFrom(xr *xdr.Reader) error { + o.I = int(xr.ReadUint64()) + o.I8 = int8(xr.ReadUint8()) + o.UI8 = xr.ReadUint8() + o.I16 = int16(xr.ReadUint16()) + o.UI16 = xr.ReadUint16() + o.I32 = int32(xr.ReadUint32()) + o.UI32 = xr.ReadUint32() + o.I64 = int64(xr.ReadUint64()) + o.UI64 = xr.ReadUint64() + o.BS = xr.ReadBytesMax(1024) + o.S = xr.ReadStringMax(1024) + (&o.C).DecodeXDRFrom(xr) + _SSSize := int(xr.ReadUint32()) + if _SSSize < 0 { + return xdr.ElementSizeExceeded("SS", _SSSize, 1024) + } + if _SSSize > 1024 { + return xdr.ElementSizeExceeded("SS", _SSSize, 1024) + } + o.SS = make([]string, _SSSize) + for i := range o.SS { + o.SS[i] = xr.ReadString() + } + return xr.Error() +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/generate.sh b/Godeps/_workspace/src/github.com/calmh/xdr/generate.sh new file mode 100644 index 000000000..2f348226b --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/generate.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +go run cmd/genxdr/main.go -- bench_test.go > bench_xdr_test.go +go run cmd/genxdr/main.go -- encdec_test.go > encdec_xdr_test.go diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/pad_ipdr.go b/Godeps/_workspace/src/github.com/calmh/xdr/pad_ipdr.go new file mode 100644 index 000000000..813a9b61d --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/pad_ipdr.go @@ -0,0 +1,10 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +// +build ipdr + +package xdr + +func pad(l int) int { + return 0 +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/pad_xdr.go b/Godeps/_workspace/src/github.com/calmh/xdr/pad_xdr.go new file mode 100644 index 000000000..b1cac8642 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/pad_xdr.go @@ -0,0 +1,14 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +// +build !ipdr + +package xdr + +func pad(l int) int { + d := l % 4 + if d == 0 { + return 0 + } + return 4 - d +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/reader.go b/Godeps/_workspace/src/github.com/calmh/xdr/reader.go new file mode 100644 index 000000000..feb3cd311 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/reader.go @@ -0,0 +1,171 @@ +// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file). +// All rights reserved. Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package xdr + +import ( + "fmt" + "io" + "reflect" + "unsafe" +) + +type Reader struct { + r io.Reader + err error + b [8]byte +} + +func NewReader(r io.Reader) *Reader { + return &Reader{ + r: r, + } +} + +func (r *Reader) ReadRaw(bs []byte) (int, error) { + if r.err != nil { + return 0, r.err + } + + var n int + n, r.err = io.ReadFull(r.r, bs) + return n, r.err +} + +func (r *Reader) ReadString() string { + return r.ReadStringMax(0) +} + +func (r *Reader) ReadStringMax(max int) string { + buf := r.ReadBytesMaxInto(max, nil) + bh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh := reflect.StringHeader{ + Data: bh.Data, + Len: bh.Len, + } + return *((*string)(unsafe.Pointer(&sh))) +} + +func (r *Reader) ReadBytes() []byte { + return r.ReadBytesInto(nil) +} + +func (r *Reader) ReadBytesMax(max int) []byte { + return r.ReadBytesMaxInto(max, nil) +} + +func (r *Reader) ReadBytesInto(dst []byte) []byte { + return r.ReadBytesMaxInto(0, dst) +} + +func (r *Reader) ReadBytesMaxInto(max int, dst []byte) []byte { + if r.err != nil { + return nil + } + + l := int(r.ReadUint32()) + if r.err != nil { + return nil + } + if l < 0 || max > 0 && l > max { + // l may be negative on 32 bit builds + r.err = ElementSizeExceeded("bytes field", l, max) + return nil + } + + if fullLen := l + pad(l); fullLen > len(dst) { + dst = make([]byte, fullLen) + } else { + dst = dst[:fullLen] + } + + var n int + n, r.err = io.ReadFull(r.r, dst) + if r.err != nil { + if debug { + dl.Printf("rd bytes (%d): %v", len(dst), r.err) + } + return nil + } + + if debug { + if n > maxDebugBytes { + dl.Printf("rd bytes (%d): %x...", len(dst), dst[:maxDebugBytes]) + } else { + dl.Printf("rd bytes (%d): %x", len(dst), dst) + } + } + return dst[:l] +} + +func (r *Reader) ReadBool() bool { + return r.ReadUint8() != 0 +} + +func (r *Reader) ReadUint32() uint32 { + if r.err != nil { + return 0 + } + + _, r.err = io.ReadFull(r.r, r.b[:4]) + if r.err != nil { + if debug { + dl.Printf("rd uint32: %v", r.err) + } + return 0 + } + + v := uint32(r.b[3]) | uint32(r.b[2])<<8 | uint32(r.b[1])<<16 | uint32(r.b[0])<<24 + + if debug { + dl.Printf("rd uint32=%d (0x%08x)", v, v) + } + return v +} + +func (r *Reader) ReadUint64() uint64 { + if r.err != nil { + return 0 + } + + _, r.err = io.ReadFull(r.r, r.b[:8]) + if r.err != nil { + if debug { + dl.Printf("rd uint64: %v", r.err) + } + return 0 + } + + v := uint64(r.b[7]) | uint64(r.b[6])<<8 | uint64(r.b[5])<<16 | uint64(r.b[4])<<24 | + uint64(r.b[3])<<32 | uint64(r.b[2])<<40 | uint64(r.b[1])<<48 | uint64(r.b[0])<<56 + + if debug { + dl.Printf("rd uint64=%d (0x%016x)", v, v) + } + return v +} + +type XDRError struct { + op string + err error +} + +func (e XDRError) Error() string { + return "xdr " + e.op + ": " + e.err.Error() +} + +func (e XDRError) IsEOF() bool { + return e.err == io.EOF +} + +func (r *Reader) Error() error { + if r.err == nil { + return nil + } + return XDRError{"read", r.err} +} + +func ElementSizeExceeded(field string, size, limit int) error { + return fmt.Errorf("%s exceeds size limit; %d > %d", field, size, limit) +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/reader_ipdr.go b/Godeps/_workspace/src/github.com/calmh/xdr/reader_ipdr.go new file mode 100644 index 000000000..28e0b56ad --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/reader_ipdr.go @@ -0,0 +1,49 @@ +// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file). +// All rights reserved. Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// +build ipdr + +package xdr + +import "io" + +func (r *Reader) ReadUint8() uint8 { + if r.err != nil { + return 0 + } + + _, r.err = io.ReadFull(r.r, r.b[:1]) + if r.err != nil { + if debug { + dl.Printf("rd uint8: %v", r.err) + } + return 0 + } + + if debug { + dl.Printf("rd uint8=%d (0x%02x)", r.b[0], r.b[0]) + } + return r.b[0] +} + +func (r *Reader) ReadUint16() uint16 { + if r.err != nil { + return 0 + } + + _, r.err = io.ReadFull(r.r, r.b[:2]) + if r.err != nil { + if debug { + dl.Printf("rd uint16: %v", r.err) + } + return 0 + } + + v := uint16(r.b[1]) | uint16(r.b[0])<<8 + + if debug { + dl.Printf("rd uint16=%d (0x%04x)", v, v) + } + return v +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/reader_xdr.go b/Godeps/_workspace/src/github.com/calmh/xdr/reader_xdr.go new file mode 100644 index 000000000..4ac629a06 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/reader_xdr.go @@ -0,0 +1,15 @@ +// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file). +// All rights reserved. Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// +build !ipdr + +package xdr + +func (r *Reader) ReadUint8() uint8 { + return uint8(r.ReadUint32()) +} + +func (r *Reader) ReadUint16() uint16 { + return uint16(r.ReadUint32()) +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/refl_test.go b/Godeps/_workspace/src/github.com/calmh/xdr/refl_test.go new file mode 100644 index 000000000..b77edd916 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/refl_test.go @@ -0,0 +1,44 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +// +build refl + +package xdr_test + +import ( + "bytes" + "testing" + + refl "github.com/davecgh/go-xdr/xdr" +) + +func TestCompareMarshals(t *testing.T) { + e0 := s.MarshalXDR() + e1, err := refl.Marshal(s) + if err != nil { + t.Fatal(err) + } + if bytes.Compare(e0, e1) != 0 { + t.Fatalf("Encoding mismatch;\n\t%x (this)\n\t%x (refl)", e0, e1) + } +} + +func BenchmarkReflMarshal(b *testing.B) { + var err error + for i := 0; i < b.N; i++ { + res, err = refl.Marshal(s) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkReflUnmarshal(b *testing.B) { + var t XDRBenchStruct + for i := 0; i < b.N; i++ { + _, err := refl.Unmarshal(e, &t) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/writer.go b/Godeps/_workspace/src/github.com/calmh/xdr/writer.go new file mode 100644 index 000000000..d1ec47bcb --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/writer.go @@ -0,0 +1,146 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +package xdr + +import ( + "io" + "reflect" + "unsafe" +) + +var padBytes = []byte{0, 0, 0} + +type Writer struct { + w io.Writer + tot int + err error + b [8]byte +} + +type AppendWriter []byte + +func (w *AppendWriter) Write(bs []byte) (int, error) { + *w = append(*w, bs...) + return len(bs), nil +} + +func NewWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + } +} + +func (w *Writer) WriteRaw(bs []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + + var n int + n, w.err = w.w.Write(bs) + return n, w.err +} + +func (w *Writer) WriteString(s string) (int, error) { + sh := *((*reflect.StringHeader)(unsafe.Pointer(&s))) + bh := reflect.SliceHeader{ + Data: sh.Data, + Len: sh.Len, + Cap: sh.Len, + } + return w.WriteBytes(*(*[]byte)(unsafe.Pointer(&bh))) +} + +func (w *Writer) WriteBytes(bs []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + + w.WriteUint32(uint32(len(bs))) + if w.err != nil { + return 0, w.err + } + + if debug { + if len(bs) > maxDebugBytes { + dl.Printf("wr bytes (%d): %x...", len(bs), bs[:maxDebugBytes]) + } else { + dl.Printf("wr bytes (%d): %x", len(bs), bs) + } + } + + var l, n int + n, w.err = w.w.Write(bs) + l += n + + if p := pad(len(bs)); w.err == nil && p > 0 { + n, w.err = w.w.Write(padBytes[:p]) + l += n + } + + w.tot += l + return l, w.err +} + +func (w *Writer) WriteBool(v bool) (int, error) { + if v { + return w.WriteUint8(1) + } else { + return w.WriteUint8(0) + } +} + +func (w *Writer) WriteUint32(v uint32) (int, error) { + if w.err != nil { + return 0, w.err + } + + if debug { + dl.Printf("wr uint32=%d", v) + } + + w.b[0] = byte(v >> 24) + w.b[1] = byte(v >> 16) + w.b[2] = byte(v >> 8) + w.b[3] = byte(v) + + var l int + l, w.err = w.w.Write(w.b[:4]) + w.tot += l + return l, w.err +} + +func (w *Writer) WriteUint64(v uint64) (int, error) { + if w.err != nil { + return 0, w.err + } + + if debug { + dl.Printf("wr uint64=%d", v) + } + + w.b[0] = byte(v >> 56) + w.b[1] = byte(v >> 48) + w.b[2] = byte(v >> 40) + w.b[3] = byte(v >> 32) + w.b[4] = byte(v >> 24) + w.b[5] = byte(v >> 16) + w.b[6] = byte(v >> 8) + w.b[7] = byte(v) + + var l int + l, w.err = w.w.Write(w.b[:8]) + w.tot += l + return l, w.err +} + +func (w *Writer) Tot() int { + return w.tot +} + +func (w *Writer) Error() error { + if w.err == nil { + return nil + } + return XDRError{"write", w.err} +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/writer_ipdr.go b/Godeps/_workspace/src/github.com/calmh/xdr/writer_ipdr.go new file mode 100644 index 000000000..df803ae33 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/writer_ipdr.go @@ -0,0 +1,41 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +// +build ipdr + +package xdr + +func (w *Writer) WriteUint8(v uint8) (int, error) { + if w.err != nil { + return 0, w.err + } + + if debug { + dl.Printf("wr uint8=%d", v) + } + + w.b[0] = byte(v) + + var l int + l, w.err = w.w.Write(w.b[:1]) + w.tot += l + return l, w.err +} + +func (w *Writer) WriteUint16(v uint16) (int, error) { + if w.err != nil { + return 0, w.err + } + + if debug { + dl.Printf("wr uint8=%d", v) + } + + w.b[0] = byte(v >> 8) + w.b[1] = byte(v) + + var l int + l, w.err = w.w.Write(w.b[:2]) + w.tot += l + return l, w.err +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/writer_xdr.go b/Godeps/_workspace/src/github.com/calmh/xdr/writer_xdr.go new file mode 100644 index 000000000..190fd5494 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/writer_xdr.go @@ -0,0 +1,14 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +// +build !ipdr + +package xdr + +func (w *Writer) WriteUint8(v uint8) (int, error) { + return w.WriteUint32(uint32(v)) +} + +func (w *Writer) WriteUint16(v uint16) (int, error) { + return w.WriteUint32(uint32(v)) +} diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/xdr_test.go b/Godeps/_workspace/src/github.com/calmh/xdr/xdr_test.go new file mode 100644 index 000000000..7744db2c6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/calmh/xdr/xdr_test.go @@ -0,0 +1,93 @@ +// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code +// is governed by an MIT-style license that can be found in the LICENSE file. + +package xdr + +import ( + "bytes" + "strings" + "testing" + "testing/quick" +) + +func TestBytesNil(t *testing.T) { + fn := func(bs []byte) bool { + var b = new(bytes.Buffer) + var w = NewWriter(b) + var r = NewReader(b) + w.WriteBytes(bs) + w.WriteBytes(bs) + r.ReadBytes() + res := r.ReadBytes() + return bytes.Compare(bs, res) == 0 + } + if err := quick.Check(fn, nil); err != nil { + t.Error(err) + } +} + +func TestBytesGiven(t *testing.T) { + fn := func(bs []byte) bool { + var b = new(bytes.Buffer) + var w = NewWriter(b) + var r = NewReader(b) + w.WriteBytes(bs) + w.WriteBytes(bs) + res := make([]byte, 12) + res = r.ReadBytesInto(res) + res = r.ReadBytesInto(res) + return bytes.Compare(bs, res) == 0 + } + if err := quick.Check(fn, nil); err != nil { + t.Error(err) + } +} + +func TestReadBytesMaxInto(t *testing.T) { + var max = 64 + for tot := 32; tot < 128; tot++ { + for diff := -32; diff <= 32; diff++ { + var b = new(bytes.Buffer) + var r = NewReader(b) + var w = NewWriter(b) + + var toWrite = make([]byte, tot) + w.WriteBytes(toWrite) + + var buf = make([]byte, tot+diff) + var bs = r.ReadBytesMaxInto(max, buf) + + if tot <= max { + if read := len(bs); read != tot { + t.Errorf("Incorrect read bytes, wrote=%d, buf=%d, max=%d, read=%d", tot, tot+diff, max, read) + } + } else if !strings.Contains(r.err.Error(), "exceeds size") { + t.Errorf("Unexpected non-ErrElementSizeExceeded error for wrote=%d, max=%d: %v", tot, max, r.err) + } + } + } +} + +func TestReadStringMax(t *testing.T) { + for tot := 42; tot < 72; tot++ { + for max := 0; max < 128; max++ { + var b = new(bytes.Buffer) + var r = NewReader(b) + var w = NewWriter(b) + + var toWrite = make([]byte, tot) + w.WriteBytes(toWrite) + + var str = r.ReadStringMax(max) + var read = len(str) + + if max == 0 || tot <= max { + if read != tot { + t.Errorf("Incorrect read bytes, wrote=%d, max=%d, read=%d", tot, max, read) + } + } else if !strings.Contains(r.err.Error(), "exceeds size") { + t.Errorf("Unexpected non-ErrElementSizeExceeded error for wrote=%d, max=%d, read=%d: %v", tot, max, read, r.err) + } + } + } +} diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/.gitignore b/Godeps/_workspace/src/github.com/camlistore/lock/.gitignore new file mode 100644 index 000000000..b25c15b81 --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/COPYING b/Godeps/_workspace/src/github.com/camlistore/lock/COPYING new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/README.txt b/Godeps/_workspace/src/github.com/camlistore/lock/README.txt new file mode 100644 index 000000000..a9eeb33de --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/README.txt @@ -0,0 +1,3 @@ +File locking library. + +See http://godoc.org/github.com/camlistore/lock diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/lock.go b/Godeps/_workspace/src/github.com/camlistore/lock/lock.go new file mode 100644 index 000000000..6268527b0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/lock.go @@ -0,0 +1,158 @@ +/* +Copyright 2013 The Go Authors + +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 lock + +import ( + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "sync" +) + +// Lock locks the given file, creating the file if necessary. If the +// file already exists, it must have zero size or an error is returned. +// The lock is an exclusive lock (a write lock), but locked files +// should neither be read from nor written to. Such files should have +// zero size and only exist to co-ordinate ownership across processes. +// +// A nil Closer is returned if an error occurred. Otherwise, close that +// Closer to release the lock. +// +// On Linux, FreeBSD and OSX, a lock has the same semantics as fcntl(2)'s +// advisory locks. In particular, closing any other file descriptor for the +// same file will release the lock prematurely. +// +// Attempting to lock a file that is already locked by the current process +// has undefined behavior. +// +// On other operating systems, lock will fallback to using the presence and +// content of a file named name + '.lock' to implement locking behavior. +func Lock(name string) (io.Closer, error) { + return lockFn(name) +} + +var lockFn = lockPortable + +// Portable version not using fcntl. Doesn't handle crashes as gracefully, +// since it can leave stale lock files. +// TODO: write pid of owner to lock file and on race see if pid is +// still alive? +func lockPortable(name string) (io.Closer, error) { + absName, err := filepath.Abs(name) + if err != nil { + return nil, fmt.Errorf("can't Lock file %q: can't find abs path: %v", name, err) + } + fi, err := os.Stat(absName) + if err == nil && fi.Size() > 0 { + if isStaleLock(absName) { + os.Remove(absName) + } else { + return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name) + } + } + f, err := os.OpenFile(absName, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_EXCL, 0666) + if err != nil { + return nil, fmt.Errorf("failed to create lock file %s %v", absName, err) + } + if err := json.NewEncoder(f).Encode(&pidLockMeta{OwnerPID: os.Getpid()}); err != nil { + return nil, err + } + return &lockCloser{f: f, abs: absName}, nil +} + +type pidLockMeta struct { + OwnerPID int +} + +func isStaleLock(path string) bool { + f, err := os.Open(path) + if err != nil { + return false + } + defer f.Close() + var meta pidLockMeta + if json.NewDecoder(f).Decode(&meta) != nil { + return false + } + if meta.OwnerPID == 0 { + return false + } + p, err := os.FindProcess(meta.OwnerPID) + if err != nil { + // e.g. on Windows + return true + } + // On unix, os.FindProcess always is true, so we have to send + // it a signal to see if it's alive. + if signalZero != nil { + if p.Signal(signalZero) != nil { + return true + } + } + return false +} + +var signalZero os.Signal // nil or set by lock_sigzero.go + +type lockCloser struct { + f *os.File + abs string + once sync.Once + err error +} + +func (lc *lockCloser) Close() error { + lc.once.Do(lc.close) + return lc.err +} + +func (lc *lockCloser) close() { + if err := lc.f.Close(); err != nil { + lc.err = err + } + if err := os.Remove(lc.abs); err != nil { + lc.err = err + } +} + +var ( + lockmu sync.Mutex + locked = map[string]bool{} // abs path -> true +) + +// unlocker is used by the darwin and linux implementations with fcntl +// advisory locks. +type unlocker struct { + f *os.File + abs string +} + +func (u *unlocker) Close() error { + lockmu.Lock() + // Remove is not necessary but it's nice for us to clean up. + // If we do do this, though, it needs to be before the + // u.f.Close below. + os.Remove(u.abs) + if err := u.f.Close(); err != nil { + return err + } + delete(locked, u.abs) + lockmu.Unlock() + return nil +} diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/lock_appengine.go b/Godeps/_workspace/src/github.com/camlistore/lock/lock_appengine.go new file mode 100644 index 000000000..ab4cad6ab --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/lock_appengine.go @@ -0,0 +1,32 @@ +// +build appengine + +/* +Copyright 2013 The Go Authors + +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 lock + +import ( + "errors" + "io" +) + +func init() { + lockFn = lockAppEngine +} + +func lockAppEngine(name string) (io.Closer, error) { + return nil, errors.New("Lock not available on App Engine") +} diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/lock_darwin_amd64.go b/Godeps/_workspace/src/github.com/camlistore/lock/lock_darwin_amd64.go new file mode 100644 index 000000000..9fea51fe8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/lock_darwin_amd64.go @@ -0,0 +1,80 @@ +// +build darwin,amd64 +// +build !appengine + +/* +Copyright 2013 The Go Authors + +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 lock + +import ( + "fmt" + "io" + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func init() { + lockFn = lockFcntl +} + +func lockFcntl(name string) (io.Closer, error) { + abs, err := filepath.Abs(name) + if err != nil { + return nil, err + } + lockmu.Lock() + if locked[abs] { + lockmu.Unlock() + return nil, fmt.Errorf("file %q already locked", abs) + } + locked[abs] = true + lockmu.Unlock() + + fi, err := os.Stat(name) + if err == nil && fi.Size() > 0 { + return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name) + } + + f, err := os.Create(name) + if err != nil { + return nil, fmt.Errorf("Lock Create of %s (abs: %s) failed: %v", name, abs, err) + } + + // This type matches C's "struct flock" defined in /usr/include/sys/fcntl.h. + // TODO: move this into the standard syscall package. + k := struct { + Start uint64 // sizeof(off_t): 8 + Len uint64 // sizeof(off_t): 8 + Pid uint32 // sizeof(pid_t): 4 + Type uint16 // sizeof(short): 2 + Whence uint16 // sizeof(short): 2 + }{ + Type: syscall.F_WRLCK, + Whence: uint16(os.SEEK_SET), + Start: 0, + Len: 0, // 0 means to lock the entire file. + Pid: uint32(os.Getpid()), + } + + _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, f.Fd(), uintptr(syscall.F_SETLK), uintptr(unsafe.Pointer(&k))) + if errno != 0 { + f.Close() + return nil, errno + } + return &unlocker{f, abs}, nil +} diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/lock_freebsd.go b/Godeps/_workspace/src/github.com/camlistore/lock/lock_freebsd.go new file mode 100644 index 000000000..d3835d624 --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/lock_freebsd.go @@ -0,0 +1,79 @@ +/* +Copyright 2013 The Go Authors + +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 lock + +import ( + "fmt" + "io" + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func init() { + lockFn = lockFcntl +} + +func lockFcntl(name string) (io.Closer, error) { + abs, err := filepath.Abs(name) + if err != nil { + return nil, err + } + lockmu.Lock() + if locked[abs] { + lockmu.Unlock() + return nil, fmt.Errorf("file %q already locked", abs) + } + locked[abs] = true + lockmu.Unlock() + + fi, err := os.Stat(name) + if err == nil && fi.Size() > 0 { + return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name) + } + + f, err := os.Create(name) + if err != nil { + return nil, err + } + + // This type matches C's "struct flock" defined in /usr/include/fcntl.h. + // TODO: move this into the standard syscall package. + k := struct { + Start int64 /* off_t starting offset */ + Len int64 /* off_t len = 0 means until end of file */ + Pid int32 /* pid_t lock owner */ + Type int16 /* short lock type: read/write, etc. */ + Whence int16 /* short type of l_start */ + Sysid int32 /* int remote system id or zero for local */ + }{ + Start: 0, + Len: 0, // 0 means to lock the entire file. + Pid: int32(os.Getpid()), + Type: syscall.F_WRLCK, + Whence: int16(os.SEEK_SET), + Sysid: 0, + } + + _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, f.Fd(), uintptr(syscall.F_SETLK), uintptr(unsafe.Pointer(&k))) + if errno != 0 { + f.Close() + return nil, errno + } + return &unlocker{f, abs}, nil +} diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/lock_linux_amd64.go b/Godeps/_workspace/src/github.com/camlistore/lock/lock_linux_amd64.go new file mode 100644 index 000000000..3a7eb00a6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/lock_linux_amd64.go @@ -0,0 +1,80 @@ +// +build linux,amd64 +// +build !appengine + +/* +Copyright 2013 The Go Authors + +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 lock + +import ( + "fmt" + "io" + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func init() { + lockFn = lockFcntl +} + +func lockFcntl(name string) (io.Closer, error) { + abs, err := filepath.Abs(name) + if err != nil { + return nil, err + } + lockmu.Lock() + if locked[abs] { + lockmu.Unlock() + return nil, fmt.Errorf("file %q already locked", abs) + } + locked[abs] = true + lockmu.Unlock() + + fi, err := os.Stat(name) + if err == nil && fi.Size() > 0 { + return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name) + } + + f, err := os.Create(name) + if err != nil { + return nil, err + } + + // This type matches C's "struct flock" defined in /usr/include/bits/fcntl.h. + // TODO: move this into the standard syscall package. + k := struct { + Type uint32 + Whence uint32 + Start uint64 + Len uint64 + Pid uint32 + }{ + Type: syscall.F_WRLCK, + Whence: uint32(os.SEEK_SET), + Start: 0, + Len: 0, // 0 means to lock the entire file. + Pid: uint32(os.Getpid()), + } + + _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, f.Fd(), uintptr(syscall.F_SETLK), uintptr(unsafe.Pointer(&k))) + if errno != 0 { + f.Close() + return nil, errno + } + return &unlocker{f, abs}, nil +} diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/lock_linux_arm.go b/Godeps/_workspace/src/github.com/camlistore/lock/lock_linux_arm.go new file mode 100644 index 000000000..c2a0a102e --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/lock_linux_arm.go @@ -0,0 +1,81 @@ +// +build linux,arm +// +build !appengine + +/* +Copyright 2013 The Go Authors + +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 lock + +import ( + "fmt" + "io" + "os" + "path/filepath" + "syscall" + "unsafe" +) + +func init() { + lockFn = lockFcntl +} + +func lockFcntl(name string) (io.Closer, error) { + abs, err := filepath.Abs(name) + if err != nil { + return nil, err + } + lockmu.Lock() + if locked[abs] { + lockmu.Unlock() + return nil, fmt.Errorf("file %q already locked", abs) + } + locked[abs] = true + lockmu.Unlock() + + fi, err := os.Stat(name) + if err == nil && fi.Size() > 0 { + return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name) + } + + f, err := os.Create(name) + if err != nil { + return nil, err + } + + // This type matches C's "struct flock" defined in /usr/include/bits/fcntl.h. + // TODO: move this into the standard syscall package. + k := struct { + Type uint16 + Whence uint16 + Start uint32 + Len uint32 + Pid uint32 + }{ + Type: syscall.F_WRLCK, + Whence: uint16(os.SEEK_SET), + Start: 0, + Len: 0, // 0 means to lock the entire file. + Pid: uint32(os.Getpid()), + } + + const F_SETLK = 6 // actual value. syscall package is wrong: golang.org/issue/7059 + _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, f.Fd(), uintptr(F_SETLK), uintptr(unsafe.Pointer(&k))) + if errno != 0 { + f.Close() + return nil, errno + } + return &unlocker{f, abs}, nil +} diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/lock_plan9.go b/Godeps/_workspace/src/github.com/camlistore/lock/lock_plan9.go new file mode 100644 index 000000000..bdf4e2292 --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/lock_plan9.go @@ -0,0 +1,55 @@ +/* +Copyright 2013 The Go Authors + +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 lock + +import ( + "fmt" + "io" + "os" + "path/filepath" +) + +func init() { + lockFn = lockPlan9 +} + +func lockPlan9(name string) (io.Closer, error) { + var f *os.File + abs, err := filepath.Abs(name) + if err != nil { + return nil, err + } + lockmu.Lock() + if locked[abs] { + lockmu.Unlock() + return nil, fmt.Errorf("file %q already locked", abs) + } + locked[abs] = true + lockmu.Unlock() + + fi, err := os.Stat(name) + if err == nil && fi.Size() > 0 { + return nil, fmt.Errorf("can't Lock file %q: has non-zero size", name) + } + + f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE, os.ModeExclusive|0644) + if err != nil { + return nil, fmt.Errorf("Lock Create of %s (abs: %s) failed: %v", name, abs, err) + } + + return &unlocker{f, abs}, nil +} diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/lock_sigzero.go b/Godeps/_workspace/src/github.com/camlistore/lock/lock_sigzero.go new file mode 100644 index 000000000..fd3ba2db1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/lock_sigzero.go @@ -0,0 +1,26 @@ +// +build !appengine +// +build linux darwin freebsd openbsd netbsd dragonfly + +/* +Copyright 2013 The Go Authors + +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 lock + +import "syscall" + +func init() { + signalZero = syscall.Signal(0) +} diff --git a/Godeps/_workspace/src/github.com/camlistore/lock/lock_test.go b/Godeps/_workspace/src/github.com/camlistore/lock/lock_test.go new file mode 100644 index 000000000..518d2f025 --- /dev/null +++ b/Godeps/_workspace/src/github.com/camlistore/lock/lock_test.go @@ -0,0 +1,131 @@ +/* +Copyright 2013 The Go Authors + +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 lock + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strconv" + "testing" +) + +func TestLock(t *testing.T) { + testLock(t, false) +} + +func TestLockPortable(t *testing.T) { + testLock(t, true) +} + +func TestLockInChild(t *testing.T) { + f := os.Getenv("TEST_LOCK_FILE") + if f == "" { + // not child + return + } + lock := Lock + if v, _ := strconv.ParseBool(os.Getenv("TEST_LOCK_PORTABLE")); v { + lock = lockPortable + } + + lk, err := lock(f) + if err != nil { + log.Fatalf("Lock failed: %v", err) + } + + if v, _ := strconv.ParseBool(os.Getenv("TEST_LOCK_CRASH")); v { + // Simulate a crash, or at least not unlocking the + // lock. We still exit 0 just to simplify the parent + // process exec code. + os.Exit(0) + } + lk.Close() +} + +func testLock(t *testing.T, portable bool) { + lock := Lock + if portable { + lock = lockPortable + } + + td, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(td) + + path := filepath.Join(td, "foo.lock") + + childLock := func(crash bool) error { + cmd := exec.Command(os.Args[0], "-test.run=LockInChild$") + cmd.Env = []string{"TEST_LOCK_FILE=" + path} + if portable { + cmd.Env = append(cmd.Env, "TEST_LOCK_PORTABLE=1") + } + if crash { + cmd.Env = append(cmd.Env, "TEST_LOCK_CRASH=1") + } + out, err := cmd.CombinedOutput() + t.Logf("Child output: %q (err %v)", out, err) + if err != nil { + return fmt.Errorf("Child Process lock of %s failed: %v %s", path, err, out) + } + return nil + } + + t.Logf("Locking in crashing child...") + if err := childLock(true); err != nil { + t.Fatalf("first lock in child process: %v", err) + } + + t.Logf("Locking+unlocking in child...") + if err := childLock(false); err != nil { + t.Fatalf("lock in child process after crashing child: %v", err) + } + + t.Logf("Locking in parent...") + lk1, err := lock(path) + if err != nil { + t.Fatal(err) + } + + t.Logf("Again in parent...") + _, err = lock(path) + if err == nil { + t.Fatal("expected second lock to fail") + } + + t.Logf("Locking in child...") + if childLock(false) == nil { + t.Fatalf("expected lock in child process to fail") + } + + t.Logf("Unlocking lock in parent") + if err := lk1.Close(); err != nil { + t.Fatal(err) + } + + lk3, err := lock(path) + if err != nil { + t.Fatal(err) + } + lk3.Close() +} diff --git a/Godeps/_workspace/src/github.com/cznic/b/AUTHORS b/Godeps/_workspace/src/github.com/cznic/b/AUTHORS new file mode 100644 index 000000000..0078f5f5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/AUTHORS @@ -0,0 +1,11 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/b/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/b/CONTRIBUTORS new file mode 100644 index 000000000..094c323ef --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/CONTRIBUTORS @@ -0,0 +1,11 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Brian Fallik +Dan Kortschak +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/b/LICENSE b/Godeps/_workspace/src/github.com/cznic/b/LICENSE new file mode 100644 index 000000000..54c6e9087 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The b Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/b/Makefile b/Godeps/_workspace/src/github.com/cznic/b/Makefile new file mode 100644 index 000000000..b02c86dbe --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/Makefile @@ -0,0 +1,53 @@ +# Copyright 2014 The b Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all todo clean cover generic mem nuke cpu + +testbin=b.test + +all: editor + go build + go vet + golint . + go install + make todo + +editor: + gofmt -l -s -w . + go test -i + go test + +clean: + @go clean + rm -f *~ *.out $(testbin) + +cover: + t=$(shell tempfile) ; go test -coverprofile $$t && go tool cover -html $$t && unlink $$t + +cpu: + go test -c + ./$(testbin) -test.cpuprofile cpu.out + go tool pprof --lines $(testbin) cpu.out + +generic: + @# writes to stdout a version where the type of key is KEY and the type + @# of value is VALUE. + @# + @# Intended use is to replace all textual occurrences of KEY or VALUE in + @# the output with your desired types. + @sed -e 's|interface{}[^{]*/\*K\*/|KEY|g' -e 's|interface{}[^{]*/\*V\*/|VALUE|g' btree.go + +mem: + go test -c + ./$(testbin) -test.bench . -test.memprofile mem.out -test.memprofilerate 1 + go tool pprof --lines --web --alloc_space $(testbin) mem.out + +nuke: clean + rm -f *.test *.out + +todo: + @grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* *.go || true + @grep -n TODO *.go || true + @grep -n BUG *.go || true + @grep -n println *.go || true diff --git a/Godeps/_workspace/src/github.com/cznic/b/README.md b/Godeps/_workspace/src/github.com/cznic/b/README.md new file mode 100644 index 000000000..5a2d80302 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/README.md @@ -0,0 +1,10 @@ +b += + +Package b implements a B+tree. + +Installation: + + $ go get github.com/cznic/b + +Documentation: [godoc.org/github.com/cznic/b](http://godoc.org/github.com/cznic/b) diff --git a/Godeps/_workspace/src/github.com/cznic/b/all_test.go b/Godeps/_workspace/src/github.com/cznic/b/all_test.go new file mode 100644 index 000000000..38c28709c --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/all_test.go @@ -0,0 +1,1300 @@ +// Copyright 2014 The b Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import ( + "bytes" + "fmt" + "io" + "math" + "path" + "runtime" + "runtime/debug" + "strings" + "testing" + + "github.com/cznic/mathutil" + "github.com/cznic/strutil" +) + +var caller = func(s string, va ...interface{}) { + _, fn, fl, _ := runtime.Caller(2) + fmt.Printf("%s:%d: ", path.Base(fn), fl) + fmt.Printf(s, va...) + fmt.Println() +} + +func dbg(s string, va ...interface{}) { + if s == "" { + s = strings.Repeat("%v ", len(va)) + } + _, fn, fl, _ := runtime.Caller(1) + fmt.Printf("%s:%d: ", path.Base(fn), fl) + fmt.Printf(s, va...) + fmt.Println() +} + +func TODO(...interface{}) string { + _, fn, fl, _ := runtime.Caller(1) + return fmt.Sprintf("TODO: %s:%d:\n", path.Base(fn), fl) +} + +func use(...interface{}) {} + +// ============================================================================ + +func isNil(p interface{}) bool { + switch x := p.(type) { + case *x: + if x == nil { + return true + } + case *d: + if x == nil { + return true + } + } + return false +} + +func (t *Tree) dump() string { + var buf bytes.Buffer + f := strutil.IndentFormatter(&buf, "\t") + + num := map[interface{}]int{} + visited := map[interface{}]bool{} + + handle := func(p interface{}) int { + if isNil(p) { + return 0 + } + + if n, ok := num[p]; ok { + return n + } + + n := len(num) + 1 + num[p] = n + return n + } + + var pagedump func(interface{}, string) + pagedump = func(p interface{}, pref string) { + if isNil(p) || visited[p] { + return + } + + visited[p] = true + switch x := p.(type) { + case *x: + h := handle(p) + n := 0 + for i, v := range x.x { + if v.ch != nil || v.k != nil { + n = i + 1 + } + } + f.Format("%sX#%d(%p) n %d:%d {", pref, h, x, x.c, n) + a := []interface{}{} + for i, v := range x.x[:n] { + a = append(a, v.ch) + if i != 0 { + f.Format(" ") + } + f.Format("(C#%d K %v)", handle(v.ch), v.k) + } + f.Format("}\n") + for _, p := range a { + pagedump(p, pref+". ") + } + case *d: + h := handle(p) + n := 0 + for i, v := range x.d { + if v.k != nil || v.v != nil { + n = i + 1 + } + } + f.Format("%sD#%d(%p) P#%d N#%d n %d:%d {", pref, h, x, handle(x.p), handle(x.n), x.c, n) + for i, v := range x.d[:n] { + if i != 0 { + f.Format(" ") + } + f.Format("%v:%v", v.k, v.v) + } + f.Format("}\n") + } + } + + pagedump(t.r, "") + s := buf.String() + if s != "" { + s = s[:len(s)-1] + } + return s +} + +func rng() *mathutil.FC32 { + x, err := mathutil.NewFC32(math.MinInt32/4, math.MaxInt32/4, false) + if err != nil { + panic(err) + } + + return x +} + +func cmp(a, b interface{}) int { + return a.(int) - b.(int) +} + +func TestGet0(t *testing.T) { + r := TreeNew(cmp) + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + _, ok := r.Get(42) + if ok { + t.Fatal(ok) + } + +} + +func TestSetGet0(t *testing.T) { + r := TreeNew(cmp) + set := r.Set + set(42, 314) + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + v, ok := r.Get(42) + if !ok { + t.Fatal(ok) + } + + if g, e := v.(int), 314; g != e { + t.Fatal(g, e) + } + + set(42, 278) + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + v, ok = r.Get(42) + if !ok { + t.Fatal(ok) + } + + if g, e := v.(int), 278; g != e { + t.Fatal(g, e) + } + + set(420, 0.5) + if g, e := r.Len(), 2; g != e { + t.Fatal(g, e) + } + + v, ok = r.Get(42) + if !ok { + t.Fatal(ok) + } + + if g, e := v.(int), 278; g != e { + t.Fatal(g, e) + } + + v, ok = r.Get(420) + if !ok { + t.Fatal(ok) + } + + if g, e := v.(float64), 0.5; g != e { + t.Fatal(g, e) + } +} + +func TestSetGet1(t *testing.T) { + const N = 40000 + for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x333333, 0xcccccc, 0x314159} { + r := TreeNew(cmp) + set := r.Set + a := make([]int, N) + for i := range a { + a[i] = (i ^ x) << 1 + } + for i, k := range a { + set(k, k^x) + if g, e := r.Len(), i+1; g != e { + t.Fatal(i, g, e) + } + } + + for i, k := range a { + v, ok := r.Get(k) + if !ok { + t.Fatal(i, k, v, ok) + } + + if g, e := v.(int), k^x; g != e { + t.Fatal(i, g, e) + } + + k |= 1 + _, ok = r.Get(k) + if ok { + t.Fatal(i, k) + } + + } + + for _, k := range a { + r.Set(k, (k^x)+42) + } + + for i, k := range a { + v, ok := r.Get(k) + if !ok { + t.Fatal(i, k, v, ok) + } + + if g, e := v.(int), k^x+42; g != e { + t.Fatal(i, g, e) + } + + k |= 1 + _, ok = r.Get(k) + if ok { + t.Fatal(i, k) + } + } + } +} + +func TestPrealloc(*testing.T) { + const n = 2e6 + rng := rng() + a := make([]int, n) + for i := range a { + a[i] = rng.Next() + } + r := TreeNew(cmp) + for _, v := range a { + r.Set(v, 0) + } + r.Close() +} + +func BenchmarkSetSeq1e3(b *testing.B) { + benchmarkSetSeq(b, 1e3) +} + +func BenchmarkSetSeq1e4(b *testing.B) { + benchmarkSetSeq(b, 1e4) +} + +func BenchmarkSetSeq1e5(b *testing.B) { + benchmarkSetSeq(b, 1e5) +} + +func BenchmarkSetSeq1e6(b *testing.B) { + benchmarkSetSeq(b, 1e6) +} + +func benchmarkSetSeq(b *testing.B, n int) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + r := TreeNew(cmp) + debug.FreeOSMemory() + b.StartTimer() + for j := 0; j < n; j++ { + r.Set(j, j) + } + b.StopTimer() + r.Close() + } + b.StopTimer() +} + +func BenchmarkGetSeq1e3(b *testing.B) { + benchmarkGetSeq(b, 1e3) +} + +func BenchmarkGetSeq1e4(b *testing.B) { + benchmarkGetSeq(b, 1e4) +} + +func BenchmarkGetSeq1e5(b *testing.B) { + benchmarkGetSeq(b, 1e5) +} + +func BenchmarkGetSeq1e6(b *testing.B) { + benchmarkGetSeq(b, 1e6) +} + +func benchmarkGetSeq(b *testing.B, n int) { + r := TreeNew(cmp) + for i := 0; i < n; i++ { + r.Set(i, i) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := 0; j < n; j++ { + r.Get(j) + } + } + b.StopTimer() + r.Close() +} + +func BenchmarkSetRnd1e3(b *testing.B) { + benchmarkSetRnd(b, 1e3) +} + +func BenchmarkSetRnd1e4(b *testing.B) { + benchmarkSetRnd(b, 1e4) +} + +func BenchmarkSetRnd1e5(b *testing.B) { + benchmarkSetRnd(b, 1e5) +} + +func BenchmarkSetRnd1e6(b *testing.B) { + benchmarkSetRnd(b, 1e6) +} + +func benchmarkSetRnd(b *testing.B, n int) { + rng := rng() + a := make([]int, n) + for i := range a { + a[i] = rng.Next() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + r := TreeNew(cmp) + debug.FreeOSMemory() + b.StartTimer() + for _, v := range a { + r.Set(v, 0) + } + b.StopTimer() + r.Close() + } + b.StopTimer() +} + +func BenchmarkGetRnd1e3(b *testing.B) { + benchmarkGetRnd(b, 1e3) +} + +func BenchmarkGetRnd1e4(b *testing.B) { + benchmarkGetRnd(b, 1e4) +} + +func BenchmarkGetRnd1e5(b *testing.B) { + benchmarkGetRnd(b, 1e5) +} + +func BenchmarkGetRnd1e6(b *testing.B) { + benchmarkGetRnd(b, 1e6) +} + +func benchmarkGetRnd(b *testing.B, n int) { + r := TreeNew(cmp) + rng := rng() + a := make([]int, n) + for i := range a { + a[i] = rng.Next() + } + for _, v := range a { + r.Set(v, 0) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, v := range a { + r.Get(v) + } + } + b.StopTimer() + r.Close() +} + +func TestSetGet2(t *testing.T) { + const N = 40000 + for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x333333, 0xcccccc, 0x314159} { + rng := rng() + r := TreeNew(cmp) + set := r.Set + a := make([]int, N) + for i := range a { + a[i] = (rng.Next() ^ x) << 1 + } + for i, k := range a { + set(k, k^x) + if g, e := r.Len(), i+1; g != e { + t.Fatal(i, x, g, e) + } + } + + for i, k := range a { + v, ok := r.Get(k) + if !ok { + t.Fatal(i, k, v, ok) + } + + if g, e := v.(int), k^x; g != e { + t.Fatal(i, g, e) + } + + k |= 1 + _, ok = r.Get(k) + if ok { + t.Fatal(i, k) + } + } + + for _, k := range a { + r.Set(k, (k^x)+42) + } + + for i, k := range a { + v, ok := r.Get(k) + if !ok { + t.Fatal(i, k, v, ok) + } + + if g, e := v.(int), k^x+42; g != e { + t.Fatal(i, g, e) + } + + k |= 1 + _, ok = r.Get(k) + if ok { + t.Fatal(i, k) + } + } + } +} + +func TestSetGet3(t *testing.T) { + r := TreeNew(cmp) + set := r.Set + var i int + for i = 0; ; i++ { + set(i, -i) + if _, ok := r.r.(*x); ok { + break + } + } + for j := 0; j <= i; j++ { + set(j, j) + } + + for j := 0; j <= i; j++ { + v, ok := r.Get(j) + if !ok { + t.Fatal(j) + } + + if g, e := v.(int), j; g != e { + t.Fatal(g, e) + } + } +} + +func TestDelete0(t *testing.T) { + r := TreeNew(cmp) + if ok := r.Delete(0); ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + r.Set(0, 0) + if ok := r.Delete(1); ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(0); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(0); ok { + t.Fatal(ok) + } + + r.Set(0, 0) + r.Set(1, 1) + if ok := r.Delete(1); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(1); ok { + t.Fatal(ok) + } + + if ok := r.Delete(0); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(0); ok { + t.Fatal(ok) + } + + r.Set(0, 0) + r.Set(1, 1) + if ok := r.Delete(0); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(0); ok { + t.Fatal(ok) + } + + if ok := r.Delete(1); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(1); ok { + t.Fatal(ok) + } +} + +func TestDelete1(t *testing.T) { + const N = 130000 + for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x333333, 0xcccccc, 0x314159} { + r := TreeNew(cmp) + set := r.Set + a := make([]int, N) + for i := range a { + a[i] = (i ^ x) << 1 + } + for _, k := range a { + set(k, 0) + } + + for i, k := range a { + ok := r.Delete(k) + if !ok { + t.Fatal(i, x, k) + } + + if g, e := r.Len(), N-i-1; g != e { + t.Fatal(i, g, e) + } + } + } +} + +func BenchmarkDelSeq1e3(b *testing.B) { + benchmarkDelSeq(b, 1e3) +} + +func BenchmarkDelSeq1e4(b *testing.B) { + benchmarkDelSeq(b, 1e4) +} + +func BenchmarkDelSeq1e5(b *testing.B) { + benchmarkDelSeq(b, 1e5) +} + +func BenchmarkDelSeq1e6(b *testing.B) { + benchmarkDelSeq(b, 1e6) +} + +func benchmarkDelSeq(b *testing.B, n int) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + r := TreeNew(cmp) + for i := 0; i < n; i++ { + r.Set(i, i) + } + debug.FreeOSMemory() + b.StartTimer() + for j := 0; j < n; j++ { + r.Delete(j) + } + } + b.StopTimer() +} + +func BenchmarkDelRnd1e3(b *testing.B) { + benchmarkDelRnd(b, 1e3) +} + +func BenchmarkDelRnd1e4(b *testing.B) { + benchmarkDelRnd(b, 1e4) +} + +func BenchmarkDelRnd1e5(b *testing.B) { + benchmarkDelRnd(b, 1e5) +} + +func BenchmarkDelRnd1e6(b *testing.B) { + benchmarkDelRnd(b, 1e6) +} + +func benchmarkDelRnd(b *testing.B, n int) { + rng := rng() + a := make([]int, n) + for i := range a { + a[i] = rng.Next() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + r := TreeNew(cmp) + for _, v := range a { + r.Set(v, 0) + } + debug.FreeOSMemory() + b.StartTimer() + for _, v := range a { + r.Delete(v) + } + b.StopTimer() + r.Close() + } + b.StopTimer() +} + +func TestDelete2(t *testing.T) { + const N = 100000 + for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x333333, 0xcccccc, 0x314159} { + r := TreeNew(cmp) + set := r.Set + a := make([]int, N) + rng := rng() + for i := range a { + a[i] = (rng.Next() ^ x) << 1 + } + for _, k := range a { + set(k, 0) + } + + for i, k := range a { + ok := r.Delete(k) + if !ok { + t.Fatal(i, x, k) + } + + if g, e := r.Len(), N-i-1; g != e { + t.Fatal(i, g, e) + } + } + } +} + +func TestEnumeratorNext(t *testing.T) { + // seeking within 3 keys: 10, 20, 30 + table := []struct { + k int + hit bool + keys []int + }{ + {5, false, []int{10, 20, 30}}, + {10, true, []int{10, 20, 30}}, + {15, false, []int{20, 30}}, + {20, true, []int{20, 30}}, + {25, false, []int{30}}, + {30, true, []int{30}}, + {35, false, []int{}}, + } + + for i, test := range table { + up := test.keys + r := TreeNew(cmp) + + r.Set(10, 100) + r.Set(20, 200) + r.Set(30, 300) + + for verChange := 0; verChange < 16; verChange++ { + en, hit := r.Seek(test.k) + + if g, e := hit, test.hit; g != e { + t.Fatal(i, g, e) + } + + j := 0 + for { + if verChange&(1<= len(up) { + t.Fatal(i, j, verChange) + } + + if g, e := k.(int), up[j]; g != e { + t.Fatal(i, j, verChange, g, e) + } + + if g, e := v.(int), 10*up[j]; g != e { + t.Fatal(i, g, e) + } + + j++ + + } + + if g, e := j, len(up); g != e { + t.Fatal(i, j, g, e) + } + } + + } +} + +func TestEnumeratorPrev(t *testing.T) { + // seeking within 3 keys: 10, 20, 30 + table := []struct { + k int + hit bool + keys []int + }{ + {5, false, []int{10}}, + {10, true, []int{10}}, + {15, false, []int{20, 10}}, + {20, true, []int{20, 10}}, + {25, false, []int{30, 20, 10}}, + {30, true, []int{30, 20, 10}}, + {35, false, []int{}}, + } + + for i, test := range table { + dn := test.keys + r := TreeNew(cmp) + + r.Set(10, 100) + r.Set(20, 200) + r.Set(30, 300) + + for verChange := 0; verChange < 16; verChange++ { + en, hit := r.Seek(test.k) + + if g, e := hit, test.hit; g != e { + t.Fatal(i, g, e) + } + + j := 0 + for { + if verChange&(1<= len(dn) { + t.Fatal(i, j, verChange) + } + + if g, e := k.(int), dn[j]; g != e { + t.Fatal(i, j, verChange, g, e) + } + + if g, e := v.(int), 10*dn[j]; g != e { + t.Fatal(i, g, e) + } + + j++ + + } + + if g, e := j, len(dn); g != e { + t.Fatal(i, j, g, e) + } + } + + } +} + +func BenchmarkSeekSeq1e3(b *testing.B) { + benchmarkSeekSeq(b, 1e3) +} + +func BenchmarkSeekSeq1e4(b *testing.B) { + benchmarkSeekSeq(b, 1e4) +} + +func BenchmarkSeekSeq1e5(b *testing.B) { + benchmarkSeekSeq(b, 1e5) +} + +func BenchmarkSeekSeq1e6(b *testing.B) { + benchmarkSeekSeq(b, 1e6) +} + +func benchmarkSeekSeq(b *testing.B, n int) { + for i := 0; i < b.N; i++ { + b.StopTimer() + t := TreeNew(cmp) + for j := 0; j < n; j++ { + t.Set(j, 0) + } + debug.FreeOSMemory() + b.StartTimer() + for j := 0; j < n; j++ { + e, _ := t.Seek(j) + e.Close() + } + b.StopTimer() + t.Close() + } + b.StopTimer() +} + +func BenchmarkSeekRnd1e3(b *testing.B) { + benchmarkSeekRnd(b, 1e3) +} + +func BenchmarkSeekRnd1e4(b *testing.B) { + benchmarkSeekRnd(b, 1e4) +} + +func BenchmarkSeekRnd1e5(b *testing.B) { + benchmarkSeekRnd(b, 1e5) +} + +func BenchmarkSeekRnd1e6(b *testing.B) { + benchmarkSeekRnd(b, 1e6) +} + +func benchmarkSeekRnd(b *testing.B, n int) { + r := TreeNew(cmp) + rng := rng() + a := make([]int, n) + for i := range a { + a[i] = rng.Next() + } + for _, v := range a { + r.Set(v, 0) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, v := range a { + e, _ := r.Seek(v) + e.Close() + } + } + b.StopTimer() + r.Close() +} + +func BenchmarkNext1e3(b *testing.B) { + benchmarkNext(b, 1e3) +} + +func BenchmarkNext1e4(b *testing.B) { + benchmarkNext(b, 1e4) +} + +func BenchmarkNext1e5(b *testing.B) { + benchmarkNext(b, 1e5) +} + +func BenchmarkNext1e6(b *testing.B) { + benchmarkNext(b, 1e6) +} + +func benchmarkNext(b *testing.B, n int) { + t := TreeNew(cmp) + for i := 0; i < n; i++ { + t.Set(i, 0) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + en, err := t.SeekFirst() + if err != nil { + b.Fatal(err) + } + + m := 0 + for { + if _, _, err = en.Next(); err != nil { + break + } + m++ + } + if m != n { + b.Fatal(m) + } + } + b.StopTimer() + t.Close() +} + +func BenchmarkPrev1e3(b *testing.B) { + benchmarkPrev(b, 1e3) +} + +func BenchmarkPrev1e4(b *testing.B) { + benchmarkPrev(b, 1e4) +} + +func BenchmarkPrev1e5(b *testing.B) { + benchmarkPrev(b, 1e5) +} + +func BenchmarkPrev1e6(b *testing.B) { + benchmarkPrev(b, 1e6) +} + +func benchmarkPrev(b *testing.B, n int) { + t := TreeNew(cmp) + for i := 0; i < n; i++ { + t.Set(i, 0) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + en, err := t.SeekLast() + if err != nil { + b.Fatal(err) + } + + m := 0 + for { + if _, _, err = en.Prev(); err != nil { + break + } + m++ + } + if m != n { + b.Fatal(m) + } + } +} + +func TestSeekFirst0(t *testing.T) { + b := TreeNew(cmp) + _, err := b.SeekFirst() + if g, e := err, io.EOF; g != e { + t.Fatal(g, e) + } +} + +func TestSeekFirst1(t *testing.T) { + b := TreeNew(cmp) + b.Set(1, 10) + en, err := b.SeekFirst() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Next() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekFirst2(t *testing.T) { + b := TreeNew(cmp) + b.Set(1, 10) + b.Set(2, 20) + en, err := b.SeekFirst() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Next() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if k != 2 || v != 20 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekFirst3(t *testing.T) { + b := TreeNew(cmp) + b.Set(2, 20) + b.Set(3, 30) + b.Set(1, 10) + en, err := b.SeekFirst() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Next() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if k != 2 || v != 20 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if k != 3 || v != 30 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekLast0(t *testing.T) { + b := TreeNew(cmp) + _, err := b.SeekLast() + if g, e := err, io.EOF; g != e { + t.Fatal(g, e) + } +} + +func TestSeekLast1(t *testing.T) { + b := TreeNew(cmp) + b.Set(1, 10) + en, err := b.SeekLast() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Prev() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekLast2(t *testing.T) { + b := TreeNew(cmp) + b.Set(1, 10) + b.Set(2, 20) + en, err := b.SeekLast() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Prev() + if k != 2 || v != 20 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekLast3(t *testing.T) { + b := TreeNew(cmp) + b.Set(2, 20) + b.Set(3, 30) + b.Set(1, 10) + en, err := b.SeekLast() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Prev() + if k != 3 || v != 30 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if k != 2 || v != 20 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestPut(t *testing.T) { + tab := []struct { + pre []int // even index: K, odd index: V + newK int // Put(newK, ... + oldV int // Put()->oldV + exists bool // upd(exists) + write bool // upd()->write + post []int // even index: K, odd index: V + }{ + // 0 + { + []int{}, + 1, 0, false, false, + []int{}, + }, + { + []int{}, + 1, 0, false, true, + []int{1, -1}, + }, + { + []int{1, 10}, + 0, 0, false, false, + []int{1, 10}, + }, + { + []int{1, 10}, + 0, 0, false, true, + []int{0, -1, 1, 10}, + }, + { + []int{1, 10}, + 1, 10, true, false, + []int{1, 10}, + }, + + // 5 + { + []int{1, 10}, + 1, 10, true, true, + []int{1, -1}, + }, + { + []int{1, 10}, + 2, 0, false, false, + []int{1, 10}, + }, + { + []int{1, 10}, + 2, 0, false, true, + []int{1, 10, 2, -1}, + }, + } + + for iTest, test := range tab { + tr := TreeNew(cmp) + for i := 0; i < len(test.pre); i += 2 { + k, v := test.pre[i], test.pre[i+1] + tr.Set(k, v) + } + + oldV, written := tr.Put(test.newK, func(old interface{}, exists bool) (newV interface{}, write bool) { + if g, e := exists, test.exists; g != e { + t.Fatal(iTest, g, e) + } + + if exists { + if g, e := old.(int), test.oldV; g != e { + t.Fatal(iTest, g, e) + } + } + return -1, test.write + }) + if test.exists { + if g, e := oldV.(int), test.oldV; g != e { + t.Fatal(iTest, g, e) + } + } + + if g, e := written, test.write; g != e { + t.Fatal(iTest, g, e) + } + + n := len(test.post) + en, err := tr.SeekFirst() + if err != nil { + if n == 0 && err == io.EOF { + continue + } + + t.Fatal(iTest, err) + } + + for i := 0; i < len(test.post); i += 2 { + k, v, err := en.Next() + if err != nil { + t.Fatal(iTest, err) + } + + if g, e := k.(int), test.post[i]; g != e { + t.Fatal(iTest, g, e) + } + + if g, e := v.(int), test.post[i+1]; g != e { + t.Fatal(iTest, g, e) + } + } + + _, _, err = en.Next() + if g, e := err, io.EOF; g != e { + t.Fatal(iTest, g, e) + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/b/btree.go b/Godeps/_workspace/src/github.com/cznic/b/btree.go new file mode 100644 index 000000000..18cbeb8b0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/btree.go @@ -0,0 +1,929 @@ +// Copyright 2014 The b Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import ( + "fmt" + "io" + "sync" +) + +const ( + kx = 32 //TODO benchmark tune this number if using custom key/value type(s). + kd = 32 //TODO benchmark tune this number if using custom key/value type(s). +) + +func init() { + if kd < 1 { + panic(fmt.Errorf("kd %d: out of range", kd)) + } + + if kx < 2 { + panic(fmt.Errorf("kx %d: out of range", kx)) + } +} + +var ( + btDPool = sync.Pool{New: func() interface{} { return &d{} }} + btEPool = btEpool{sync.Pool{New: func() interface{} { return &Enumerator{} }}} + btTPool = btTpool{sync.Pool{New: func() interface{} { return &Tree{} }}} + btXPool = sync.Pool{New: func() interface{} { return &x{} }} +) + +type btTpool struct{ sync.Pool } + +func (p *btTpool) get(cmp Cmp) *Tree { + x := p.Get().(*Tree) + x.cmp = cmp + return x +} + +type btEpool struct{ sync.Pool } + +func (p *btEpool) get(err error, hit bool, i int, k interface{} /*K*/, q *d, t *Tree, ver int64) *Enumerator { + x := p.Get().(*Enumerator) + x.err, x.hit, x.i, x.k, x.q, x.t, x.ver = err, hit, i, k, q, t, ver + return x +} + +type ( + // Cmp compares a and b. Return value is: + // + // < 0 if a < b + // 0 if a == b + // > 0 if a > b + // + Cmp func(a, b interface{} /*K*/) int + + d struct { // data page + c int + d [2*kd + 1]de + n *d + p *d + } + + de struct { // d element + k interface{} /*K*/ + v interface{} /*V*/ + } + + // Enumerator captures the state of enumerating a tree. It is returned + // from the Seek* methods. The enumerator is aware of any mutations + // made to the tree in the process of enumerating it and automatically + // resumes the enumeration at the proper key, if possible. + // + // However, once an Enumerator returns io.EOF to signal "no more + // items", it does no more attempt to "resync" on tree mutation(s). In + // other words, io.EOF from an Enumaretor is "sticky" (idempotent). + Enumerator struct { + err error + hit bool + i int + k interface{} /*K*/ + q *d + t *Tree + ver int64 + } + + // Tree is a B+tree. + Tree struct { + c int + cmp Cmp + first *d + last *d + r interface{} + ver int64 + } + + xe struct { // x element + ch interface{} + k interface{} /*K*/ + } + + x struct { // index page + c int + x [2*kx + 2]xe + } +) + +var ( // R/O zero values + zd d + zde de + ze Enumerator + zk interface{} /*K*/ + zt Tree + zx x + zxe xe +) + +func clr(q interface{}) { + switch x := q.(type) { + case *x: + for i := 0; i <= x.c; i++ { // Ch0 Sep0 ... Chn-1 Sepn-1 Chn + clr(x.x[i].ch) + } + *x = zx + btXPool.Put(x) + case *d: + *x = zd + btDPool.Put(x) + } +} + +// -------------------------------------------------------------------------- x + +func newX(ch0 interface{}) *x { + r := btXPool.Get().(*x) + r.x[0].ch = ch0 + return r +} + +func (q *x) extract(i int) { + q.c-- + if i < q.c { + copy(q.x[i:], q.x[i+1:q.c+1]) + q.x[q.c].ch = q.x[q.c+1].ch + q.x[q.c].k = zk // GC + q.x[q.c+1] = zxe // GC + } +} + +func (q *x) insert(i int, k interface{} /*K*/, ch interface{}) *x { + c := q.c + if i < c { + q.x[c+1].ch = q.x[c].ch + copy(q.x[i+2:], q.x[i+1:c]) + q.x[i+1].k = q.x[i].k + } + c++ + q.c = c + q.x[i].k = k + q.x[i+1].ch = ch + return q +} + +func (q *x) siblings(i int) (l, r *d) { + if i >= 0 { + if i > 0 { + l = q.x[i-1].ch.(*d) + } + if i < q.c { + r = q.x[i+1].ch.(*d) + } + } + return +} + +// -------------------------------------------------------------------------- d + +func (l *d) mvL(r *d, c int) { + copy(l.d[l.c:], r.d[:c]) + copy(r.d[:], r.d[c:r.c]) + l.c += c + r.c -= c +} + +func (l *d) mvR(r *d, c int) { + copy(r.d[c:], r.d[:r.c]) + copy(r.d[:c], l.d[l.c-c:]) + r.c += c + l.c -= c +} + +// ----------------------------------------------------------------------- Tree + +// TreeNew returns a newly created, empty Tree. The compare function is used +// for key collation. +func TreeNew(cmp Cmp) *Tree { + return btTPool.get(cmp) +} + +// Clear removes all K/V pairs from the tree. +func (t *Tree) Clear() { + if t.r == nil { + return + } + + clr(t.r) + t.c, t.first, t.last, t.r = 0, nil, nil, nil + t.ver++ +} + +// Close performs Clear and recycles t to a pool for possible later reuse. No +// references to t should exist or such references must not be used afterwards. +func (t *Tree) Close() { + t.Clear() + *t = zt + btTPool.Put(t) +} + +func (t *Tree) cat(p *x, q, r *d, pi int) { + t.ver++ + q.mvL(r, r.c) + if r.n != nil { + r.n.p = q + } else { + t.last = q + } + q.n = r.n + *r = zd + btDPool.Put(r) + if p.c > 1 { + p.extract(pi) + p.x[pi].ch = q + return + } + + switch x := t.r.(type) { + case *x: + *x = zx + btXPool.Put(x) + case *d: + *x = zd + btDPool.Put(x) + } + t.r = q +} + +func (t *Tree) catX(p, q, r *x, pi int) { + t.ver++ + q.x[q.c].k = p.x[pi].k + copy(q.x[q.c+1:], r.x[:r.c]) + q.c += r.c + 1 + q.x[q.c].ch = r.x[r.c].ch + *r = zx + btXPool.Put(r) + if p.c > 1 { + p.c-- + pc := p.c + if pi < pc { + p.x[pi].k = p.x[pi+1].k + copy(p.x[pi+1:], p.x[pi+2:pc+1]) + p.x[pc].ch = p.x[pc+1].ch + p.x[pc].k = zk // GC + p.x[pc+1].ch = nil // GC + } + return + } + + switch x := t.r.(type) { + case *x: + *x = zx + btXPool.Put(x) + case *d: + *x = zd + btDPool.Put(x) + } + t.r = q +} + +// Delete removes the k's KV pair, if it exists, in which case Delete returns +// true. +func (t *Tree) Delete(k interface{} /*K*/) (ok bool) { + pi := -1 + var p *x + q := t.r + if q == nil { + return false + } + + for { + var i int + i, ok = t.find(q, k) + if ok { + switch x := q.(type) { + case *x: + if x.c < kx && q != t.r { + x, i = t.underflowX(p, x, pi, i) + } + pi = i + 1 + p = x + q = x.x[pi].ch + ok = false + continue + case *d: + t.extract(x, i) + if x.c >= kd { + return true + } + + if q != t.r { + t.underflow(p, x, pi) + } else if t.c == 0 { + t.Clear() + } + return true + } + } + + switch x := q.(type) { + case *x: + if x.c < kx && q != t.r { + x, i = t.underflowX(p, x, pi, i) + } + pi = i + p = x + q = x.x[i].ch + case *d: + return false + } + } +} + +func (t *Tree) extract(q *d, i int) { // (r interface{} /*V*/) { + t.ver++ + //r = q.d[i].v // prepared for Extract + q.c-- + if i < q.c { + copy(q.d[i:], q.d[i+1:q.c+1]) + } + q.d[q.c] = zde // GC + t.c-- + return +} + +func (t *Tree) find(q interface{}, k interface{} /*K*/) (i int, ok bool) { + var mk interface{} /*K*/ + l := 0 + switch x := q.(type) { + case *x: + h := x.c - 1 + for l <= h { + m := (l + h) >> 1 + mk = x.x[m].k + switch cmp := t.cmp(k, mk); { + case cmp > 0: + l = m + 1 + case cmp == 0: + return m, true + default: + h = m - 1 + } + } + case *d: + h := x.c - 1 + for l <= h { + m := (l + h) >> 1 + mk = x.d[m].k + switch cmp := t.cmp(k, mk); { + case cmp > 0: + l = m + 1 + case cmp == 0: + return m, true + default: + h = m - 1 + } + } + } + return l, false +} + +// First returns the first item of the tree in the key collating order, or +// (zero-value, zero-value) if the tree is empty. +func (t *Tree) First() (k interface{} /*K*/, v interface{} /*V*/) { + if q := t.first; q != nil { + q := &q.d[0] + k, v = q.k, q.v + } + return +} + +// Get returns the value associated with k and true if it exists. Otherwise Get +// returns (zero-value, false). +func (t *Tree) Get(k interface{} /*K*/) (v interface{} /*V*/, ok bool) { + q := t.r + if q == nil { + return + } + + for { + var i int + if i, ok = t.find(q, k); ok { + switch x := q.(type) { + case *x: + q = x.x[i+1].ch + continue + case *d: + return x.d[i].v, true + } + } + switch x := q.(type) { + case *x: + q = x.x[i].ch + default: + return + } + } +} + +func (t *Tree) insert(q *d, i int, k interface{} /*K*/, v interface{} /*V*/) *d { + t.ver++ + c := q.c + if i < c { + copy(q.d[i+1:], q.d[i:c]) + } + c++ + q.c = c + q.d[i].k, q.d[i].v = k, v + t.c++ + return q +} + +// Last returns the last item of the tree in the key collating order, or +// (zero-value, zero-value) if the tree is empty. +func (t *Tree) Last() (k interface{} /*K*/, v interface{} /*V*/) { + if q := t.last; q != nil { + q := &q.d[q.c-1] + k, v = q.k, q.v + } + return +} + +// Len returns the number of items in the tree. +func (t *Tree) Len() int { + return t.c +} + +func (t *Tree) overflow(p *x, q *d, pi, i int, k interface{} /*K*/, v interface{} /*V*/) { + t.ver++ + l, r := p.siblings(pi) + + if l != nil && l.c < 2*kd { + l.mvL(q, 1) + t.insert(q, i-1, k, v) + p.x[pi-1].k = q.d[0].k + return + } + + if r != nil && r.c < 2*kd { + if i < 2*kd { + q.mvR(r, 1) + t.insert(q, i, k, v) + p.x[pi].k = r.d[0].k + return + } + + t.insert(r, 0, k, v) + p.x[pi].k = k + return + } + + t.split(p, q, pi, i, k, v) +} + +// Seek returns an Enumerator positioned on a an item such that k >= item's +// key. ok reports if k == item.key The Enumerator's position is possibly +// after the last item in the tree. +func (t *Tree) Seek(k interface{} /*K*/) (e *Enumerator, ok bool) { + q := t.r + if q == nil { + e = btEPool.get(nil, false, 0, k, nil, t, t.ver) + return + } + + for { + var i int + if i, ok = t.find(q, k); ok { + switch x := q.(type) { + case *x: + q = x.x[i+1].ch + continue + case *d: + return btEPool.get(nil, ok, i, k, x, t, t.ver), true + } + } + + switch x := q.(type) { + case *x: + q = x.x[i].ch + case *d: + return btEPool.get(nil, ok, i, k, x, t, t.ver), false + } + } +} + +// SeekFirst returns an enumerator positioned on the first KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returned and e will be nil. +func (t *Tree) SeekFirst() (e *Enumerator, err error) { + q := t.first + if q == nil { + return nil, io.EOF + } + + return btEPool.get(nil, true, 0, q.d[0].k, q, t, t.ver), nil +} + +// SeekLast returns an enumerator positioned on the last KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returned and e will be nil. +func (t *Tree) SeekLast() (e *Enumerator, err error) { + q := t.last + if q == nil { + return nil, io.EOF + } + + return btEPool.get(nil, true, q.c-1, q.d[q.c-1].k, q, t, t.ver), nil +} + +// Set sets the value associated with k. +func (t *Tree) Set(k interface{} /*K*/, v interface{} /*V*/) { + //dbg("--- PRE Set(%v, %v)\n%s", k, v, t.dump()) + //defer func() { + // dbg("--- POST\n%s\n====\n", t.dump()) + //}() + + pi := -1 + var p *x + q := t.r + if q == nil { + z := t.insert(btDPool.Get().(*d), 0, k, v) + t.r, t.first, t.last = z, z, z + return + } + + for { + i, ok := t.find(q, k) + if ok { + switch x := q.(type) { + case *x: + if x.c > 2*kx { + x, i = t.splitX(p, x, pi, i) + } + pi = i + 1 + p = x + q = x.x[i+1].ch + continue + case *d: + x.d[i].v = v + } + return + } + + switch x := q.(type) { + case *x: + if x.c > 2*kx { + x, i = t.splitX(p, x, pi, i) + } + pi = i + p = x + q = x.x[i].ch + case *d: + switch { + case x.c < 2*kd: + t.insert(x, i, k, v) + default: + t.overflow(p, x, pi, i, k, v) + } + return + } + } +} + +// Put combines Get and Set in a more efficient way where the tree is walked +// only once. The upd(ater) receives (old-value, true) if a KV pair for k +// exists or (zero-value, false) otherwise. It can then return a (new-value, +// true) to create or overwrite the existing value in the KV pair, or +// (whatever, false) if it decides not to create or not to update the value of +// the KV pair. +// +// tree.Set(k, v) call conceptually equals calling +// +// tree.Put(k, func(interface{} /*K*/, bool){ return v, true }) +// +// modulo the differing return values. +func (t *Tree) Put(k interface{} /*K*/, upd func(oldV interface{} /*V*/, exists bool) (newV interface{} /*V*/, write bool)) (oldV interface{} /*V*/, written bool) { + pi := -1 + var p *x + q := t.r + var newV interface{} /*V*/ + if q == nil { + // new KV pair in empty tree + newV, written = upd(newV, false) + if !written { + return + } + + z := t.insert(btDPool.Get().(*d), 0, k, newV) + t.r, t.first, t.last = z, z, z + return + } + + for { + i, ok := t.find(q, k) + if ok { + switch x := q.(type) { + case *x: + if x.c > 2*kx { + x, i = t.splitX(p, x, pi, i) + } + pi = i + 1 + p = x + q = x.x[i+1].ch + continue + case *d: + oldV = x.d[i].v + newV, written = upd(oldV, true) + if !written { + return + } + + x.d[i].v = newV + } + return + } + + switch x := q.(type) { + case *x: + if x.c > 2*kx { + x, i = t.splitX(p, x, pi, i) + } + pi = i + p = x + q = x.x[i].ch + case *d: // new KV pair + newV, written = upd(newV, false) + if !written { + return + } + + switch { + case x.c < 2*kd: + t.insert(x, i, k, newV) + default: + t.overflow(p, x, pi, i, k, newV) + } + return + } + } +} + +func (t *Tree) split(p *x, q *d, pi, i int, k interface{} /*K*/, v interface{} /*V*/) { + t.ver++ + r := btDPool.Get().(*d) + if q.n != nil { + r.n = q.n + r.n.p = r + } else { + t.last = r + } + q.n = r + r.p = q + + copy(r.d[:], q.d[kd:2*kd]) + for i := range q.d[kd:] { + q.d[kd+i] = zde + } + q.c = kd + r.c = kd + var done bool + if i > kd { + done = true + t.insert(r, i-kd, k, v) + } + if pi >= 0 { + p.insert(pi, r.d[0].k, r) + } else { + t.r = newX(q).insert(0, r.d[0].k, r) + } + if done { + return + } + + t.insert(q, i, k, v) +} + +func (t *Tree) splitX(p *x, q *x, pi int, i int) (*x, int) { + t.ver++ + r := btXPool.Get().(*x) + copy(r.x[:], q.x[kx+1:]) + q.c = kx + r.c = kx + if pi >= 0 { + p.insert(pi, q.x[kx].k, r) + q.x[kx].k = zk + for i := range q.x[kx+1:] { + q.x[kx+i+1] = zxe + } + + switch { + case i < kx: + return q, i + case i == kx: + return p, pi + default: // i > kx + return r, i - kx - 1 + } + } + + nr := newX(q).insert(0, q.x[kx].k, r) + t.r = nr + q.x[kx].k = zk + for i := range q.x[kx+1:] { + q.x[kx+i+1] = zxe + } + + switch { + case i < kx: + return q, i + case i == kx: + return nr, 0 + default: // i > kx + return r, i - kx - 1 + } +} + +func (t *Tree) underflow(p *x, q *d, pi int) { + t.ver++ + l, r := p.siblings(pi) + + if l != nil && l.c+q.c >= 2*kd { + l.mvR(q, 1) + p.x[pi-1].k = q.d[0].k + return + } + + if r != nil && q.c+r.c >= 2*kd { + q.mvL(r, 1) + p.x[pi].k = r.d[0].k + r.d[r.c] = zde // GC + return + } + + if l != nil { + t.cat(p, l, q, pi-1) + return + } + + t.cat(p, q, r, pi) +} + +func (t *Tree) underflowX(p *x, q *x, pi int, i int) (*x, int) { + t.ver++ + var l, r *x + + if pi >= 0 { + if pi > 0 { + l = p.x[pi-1].ch.(*x) + } + if pi < p.c { + r = p.x[pi+1].ch.(*x) + } + } + + if l != nil && l.c > kx { + q.x[q.c+1].ch = q.x[q.c].ch + copy(q.x[1:], q.x[:q.c]) + q.x[0].ch = l.x[l.c].ch + q.x[0].k = p.x[pi-1].k + q.c++ + i++ + l.c-- + p.x[pi-1].k = l.x[l.c].k + return q, i + } + + if r != nil && r.c > kx { + q.x[q.c].k = p.x[pi].k + q.c++ + q.x[q.c].ch = r.x[0].ch + p.x[pi].k = r.x[0].k + copy(r.x[:], r.x[1:r.c]) + r.c-- + rc := r.c + r.x[rc].ch = r.x[rc+1].ch + r.x[rc].k = zk + r.x[rc+1].ch = nil + return q, i + } + + if l != nil { + i += l.c + 1 + t.catX(p, l, q, pi-1) + q = l + return q, i + } + + t.catX(p, q, r, pi) + return q, i +} + +// ----------------------------------------------------------------- Enumerator + +// Close recycles e to a pool for possible later reuse. No references to e +// should exist or such references must not be used afterwards. +func (e *Enumerator) Close() { + *e = ze + btEPool.Put(e) +} + +// Next returns the currently enumerated item, if it exists and moves to the +// next item in the key collation order. If there is no item to return, err == +// io.EOF is returned. +func (e *Enumerator) Next() (k interface{} /*K*/, v interface{} /*V*/, err error) { + if err = e.err; err != nil { + return + } + + if e.ver != e.t.ver { + f, hit := e.t.Seek(e.k) + if !e.hit && hit { + if err = f.next(); err != nil { + return + } + } + + *e = *f + f.Close() + } + if e.q == nil { + e.err, err = io.EOF, io.EOF + return + } + + if e.i >= e.q.c { + if err = e.next(); err != nil { + return + } + } + + i := e.q.d[e.i] + k, v = i.k, i.v + e.k, e.hit = k, false + e.next() + return +} + +func (e *Enumerator) next() error { + if e.q == nil { + e.err = io.EOF + return io.EOF + } + + switch { + case e.i < e.q.c-1: + e.i++ + default: + if e.q, e.i = e.q.n, 0; e.q == nil { + e.err = io.EOF + } + } + return e.err +} + +// Prev returns the currently enumerated item, if it exists and moves to the +// previous item in the key collation order. If there is no item to return, err +// == io.EOF is returned. +func (e *Enumerator) Prev() (k interface{} /*K*/, v interface{} /*V*/, err error) { + if err = e.err; err != nil { + return + } + + if e.ver != e.t.ver { + f, hit := e.t.Seek(e.k) + if !e.hit && hit { + if err = f.prev(); err != nil { + return + } + } + + *e = *f + f.Close() + } + if e.q == nil { + e.err, err = io.EOF, io.EOF + return + } + + if e.i >= e.q.c { + if err = e.next(); err != nil { + return + } + } + + i := e.q.d[e.i] + k, v = i.k, i.v + e.k, e.hit = k, false + e.prev() + return +} + +func (e *Enumerator) prev() error { + if e.q == nil { + e.err = io.EOF + return io.EOF + } + + switch { + case e.i > 0: + e.i-- + default: + if e.q = e.q.p; e.q == nil { + e.err = io.EOF + break + } + + e.i = e.q.c - 1 + } + return e.err +} diff --git a/Godeps/_workspace/src/github.com/cznic/b/doc.go b/Godeps/_workspace/src/github.com/cznic/b/doc.go new file mode 100644 index 000000000..ddcf73704 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/doc.go @@ -0,0 +1,53 @@ +// Copyright 2014 The b Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package b implements the B+tree flavor of a BTree. +// +// Changelog +// +// 2014-06-26: Lower GC presure by recycling things. +// +// 2014-04-18: Added new method Put. +// +// Generic types +// +// Keys and their associated values are interface{} typed, similar to all of +// the containers in the standard library. +// +// Semiautomatic production of a type specific variant of this package is +// supported via +// +// $ make generic +// +// This command will write to stdout a version of the btree.go file where every +// key type occurrence is replaced by the word 'KEY' and every value type +// occurrence is replaced by the word 'VALUE'. Then you have to replace these +// tokens with your desired type(s), using any technique you're comfortable +// with. +// +// This is how, for example, 'example/int.go' was created: +// +// $ mkdir example +// $ make generic | sed -e 's/KEY/int/g' -e 's/VALUE/int/g' > example/int.go +// +// No other changes to int.go are necessary, it compiles just fine. +// +// Running the benchmarks for 1000 keys on a machine with Intel i5-4670 CPU @ +// 3.4GHz, Go release 1.4.2. +// +// $ go test -bench 1e3 example/all_test.go example/int.go +// PASS +// BenchmarkSetSeq1e3 10000 151620 ns/op +// BenchmarkGetSeq1e3 10000 115354 ns/op +// BenchmarkSetRnd1e3 5000 255865 ns/op +// BenchmarkGetRnd1e3 10000 140466 ns/op +// BenchmarkDelSeq1e3 10000 143860 ns/op +// BenchmarkDelRnd1e3 10000 188228 ns/op +// BenchmarkSeekSeq1e3 10000 156448 ns/op +// BenchmarkSeekRnd1e3 10000 190587 ns/op +// BenchmarkNext1e3 200000 9407 ns/op +// BenchmarkPrev1e3 200000 9306 ns/op +// ok command-line-arguments 26.369s +// $ +package b diff --git a/Godeps/_workspace/src/github.com/cznic/b/example/Makefile b/Godeps/_workspace/src/github.com/cznic/b/example/Makefile new file mode 100644 index 000000000..6c0f1856d --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/example/Makefile @@ -0,0 +1,35 @@ +# Copyright 2014 The b Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all todo clean cover mem + +testbin=b.test + +all: editor + go build + go vet + make todo + +editor: + go fmt + go test -i + go test + +mem: + go test -c + ./$(testbin) -test.bench . -test.memprofile mem.out -test.memprofilerate 1 + go tool pprof --lines --web --alloc_space $(testbin) mem.out + +todo: + @grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* *.go || true + @grep -n TODO *.go || true + @grep -n BUG *.go || true + @grep -n println *.go || true + +clean: + @go clean + rm -f *~ + +cover: + t=$(shell tempfile) ; go test -coverprofile $$t && go tool cover -html $$t && unlink $$t diff --git a/Godeps/_workspace/src/github.com/cznic/b/example/all_test.go b/Godeps/_workspace/src/github.com/cznic/b/example/all_test.go new file mode 100644 index 000000000..cfc7848c9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/example/all_test.go @@ -0,0 +1,1126 @@ +// Copyright 2014 The b Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import ( + "io" + "math" + "runtime/debug" + "testing" + + "github.com/cznic/fileutil" + "github.com/cznic/mathutil" +) + +func rng() *mathutil.FC32 { + x, err := mathutil.NewFC32(math.MinInt32/4, math.MaxInt32/4, false) + if err != nil { + panic(err) + } + + return x +} + +func cmp(a, b int) int { + return a - b +} + +func TestGet0(t *testing.T) { + r := TreeNew(cmp) + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + _, ok := r.Get(42) + if ok { + t.Fatal(ok) + } + +} + +func TestSetGet0(t *testing.T) { + r := TreeNew(cmp) + set := r.Set + set(42, 314) + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + v, ok := r.Get(42) + if !ok { + t.Fatal(ok) + } + + if g, e := v, 314; g != e { + t.Fatal(g, e) + } + + set(42, 278) + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + v, ok = r.Get(42) + if !ok { + t.Fatal(ok) + } + + if g, e := v, 278; g != e { + t.Fatal(g, e) + } + + set(420, 50) + if g, e := r.Len(), 2; g != e { + t.Fatal(g, e) + } + + v, ok = r.Get(42) + if !ok { + t.Fatal(ok) + } + + if g, e := v, 278; g != e { + t.Fatal(g, e) + } + + v, ok = r.Get(420) + if !ok { + t.Fatal(ok) + } + + if g, e := v, 50; g != e { + t.Fatal(g, e) + } +} + +func TestSetGet1(t *testing.T) { + const N = 90000 + for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x314259} { + r := TreeNew(cmp) + set := r.Set + a := make([]int, N) + for i := range a { + a[i] = (i ^ x) << 1 + } + for i, k := range a { + set(k, k^x) + if g, e := r.Len(), i+1; g != e { + t.Fatal(i, g, e) + } + } + + for i, k := range a { + v, ok := r.Get(k) + if !ok { + t.Fatal(i, k, v, ok) + } + + if g, e := v, k^x; g != e { + t.Fatal(i, g, e) + } + + k |= 1 + _, ok = r.Get(k) + if ok { + t.Fatal(i, k) + } + + } + } +} + +func BenchmarkSetSeq1e3(b *testing.B) { + benchmarkSetSeq(b, 1e3) +} + +func BenchmarkSetSeq1e4(b *testing.B) { + benchmarkSetSeq(b, 1e4) +} + +func BenchmarkSetSeq1e5(b *testing.B) { + benchmarkSetSeq(b, 1e5) +} + +func BenchmarkSetSeq1e6(b *testing.B) { + benchmarkSetSeq(b, 1e6) +} + +func benchmarkSetSeq(b *testing.B, n int) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + r := TreeNew(cmp) + debug.FreeOSMemory() + b.StartTimer() + for j := 0; j < n; j++ { + r.Set(j, j) + } + b.StopTimer() + r.Close() + } + b.StopTimer() +} + +func BenchmarkGetSeq1e3(b *testing.B) { + benchmarkGetSeq(b, 1e3) +} + +func BenchmarkGetSeq1e4(b *testing.B) { + benchmarkGetSeq(b, 1e4) +} + +func BenchmarkGetSeq1e5(b *testing.B) { + benchmarkGetSeq(b, 1e5) +} + +func BenchmarkGetSeq1e6(b *testing.B) { + benchmarkGetSeq(b, 1e6) +} + +func benchmarkGetSeq(b *testing.B, n int) { + r := TreeNew(cmp) + for i := 0; i < n; i++ { + r.Set(i, i) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := 0; j < n; j++ { + r.Get(j) + } + } + b.StopTimer() + r.Close() +} + +func BenchmarkSetRnd1e3(b *testing.B) { + benchmarkSetRnd(b, 1e3) +} + +func BenchmarkSetRnd1e4(b *testing.B) { + benchmarkSetRnd(b, 1e4) +} + +func BenchmarkSetRnd1e5(b *testing.B) { + benchmarkSetRnd(b, 1e5) +} + +func BenchmarkSetRnd1e6(b *testing.B) { + benchmarkSetRnd(b, 1e6) +} + +func benchmarkSetRnd(b *testing.B, n int) { + rng := rng() + a := make([]int, n) + for i := range a { + a[i] = rng.Next() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + r := TreeNew(cmp) + debug.FreeOSMemory() + b.StartTimer() + for _, v := range a { + r.Set(v, 0) + } + b.StopTimer() + r.Close() + } + b.StopTimer() +} + +func BenchmarkGetRnd1e3(b *testing.B) { + benchmarkGetRnd(b, 1e3) +} + +func BenchmarkGetRnd1e4(b *testing.B) { + benchmarkGetRnd(b, 1e4) +} + +func BenchmarkGetRnd1e5(b *testing.B) { + benchmarkGetRnd(b, 1e5) +} + +func BenchmarkGetRnd1e6(b *testing.B) { + benchmarkGetRnd(b, 1e6) +} + +func benchmarkGetRnd(b *testing.B, n int) { + r := TreeNew(cmp) + rng := rng() + a := make([]int, n) + for i := range a { + a[i] = rng.Next() + } + for _, v := range a { + r.Set(v, 0) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, v := range a { + r.Get(v) + } + } + b.StopTimer() + r.Close() +} + +func TestSetGet2(t *testing.T) { + const N = 70000 + for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x314259} { + r := TreeNew(cmp) + set := r.Set + a := make([]int, N) + rng := rng() + for i := range a { + a[i] = (rng.Next() ^ x) << 1 + } + for i, k := range a { + set(k, k^x) + if g, e := r.Len(), i+1; g != e { + t.Fatal(i, x, g, e) + } + } + + for i, k := range a { + v, ok := r.Get(k) + if !ok { + t.Fatal(i, k, v, ok) + } + + if g, e := v, k^x; g != e { + t.Fatal(i, g, e) + } + + k |= 1 + _, ok = r.Get(k) + if ok { + t.Fatal(i, k) + } + } + } +} + +func TestSetGet3(t *testing.T) { + r := TreeNew(cmp) + set := r.Set + var i int + for i = 0; ; i++ { + set(i, -i) + if _, ok := r.r.(*x); ok { + break + } + } + for j := 0; j <= i; j++ { + set(j, j) + } + + for j := 0; j <= i; j++ { + v, ok := r.Get(j) + if !ok { + t.Fatal(j) + } + + if g, e := v, j; g != e { + t.Fatal(g, e) + } + } +} + +func TestDelete0(t *testing.T) { + r := TreeNew(cmp) + if ok := r.Delete(0); ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + r.Set(0, 0) + if ok := r.Delete(1); ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(0); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(0); ok { + t.Fatal(ok) + } + + r.Set(0, 0) + r.Set(1, 1) + if ok := r.Delete(1); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(1); ok { + t.Fatal(ok) + } + + if ok := r.Delete(0); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(0); ok { + t.Fatal(ok) + } + + r.Set(0, 0) + r.Set(1, 1) + if ok := r.Delete(0); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 1; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(0); ok { + t.Fatal(ok) + } + + if ok := r.Delete(1); !ok { + t.Fatal(ok) + } + + if g, e := r.Len(), 0; g != e { + t.Fatal(g, e) + } + + if ok := r.Delete(1); ok { + t.Fatal(ok) + } +} + +func TestDelete1(t *testing.T) { + const N = 100000 + for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x314259} { + r := TreeNew(cmp) + set := r.Set + a := make([]int, N) + for i := range a { + a[i] = (i ^ x) << 1 + } + for _, k := range a { + set(k, 0) + } + + for i, k := range a { + ok := r.Delete(k) + if !ok { + t.Fatal(i, x, k) + } + + if g, e := r.Len(), N-i-1; g != e { + t.Fatal(i, g, e) + } + } + } +} + +func BenchmarkDelSeq1e3(b *testing.B) { + benchmarkDelSeq(b, 1e3) +} + +func BenchmarkDelSeq1e4(b *testing.B) { + benchmarkDelSeq(b, 1e4) +} + +func BenchmarkDelSeq1e5(b *testing.B) { + benchmarkDelSeq(b, 1e5) +} + +func BenchmarkDelSeq1e6(b *testing.B) { + benchmarkDelSeq(b, 1e6) +} + +func benchmarkDelSeq(b *testing.B, n int) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + r := TreeNew(cmp) + for i := 0; i < n; i++ { + r.Set(i, i) + } + debug.FreeOSMemory() + b.StartTimer() + for j := 0; j < n; j++ { + r.Delete(j) + } + b.StopTimer() + r.Close() + } + b.StopTimer() +} + +func BenchmarkDelRnd1e3(b *testing.B) { + benchmarkDelRnd(b, 1e3) +} + +func BenchmarkDelRnd1e4(b *testing.B) { + benchmarkDelRnd(b, 1e4) +} + +func BenchmarkDelRnd1e5(b *testing.B) { + benchmarkDelRnd(b, 1e5) +} + +func BenchmarkDelRnd1e6(b *testing.B) { + benchmarkDelRnd(b, 1e6) +} + +func benchmarkDelRnd(b *testing.B, n int) { + rng := rng() + a := make([]int, n) + for i := range a { + a[i] = rng.Next() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + r := TreeNew(cmp) + for _, v := range a { + r.Set(v, 0) + } + debug.FreeOSMemory() + b.StartTimer() + for _, v := range a { + r.Delete(v) + } + b.StopTimer() + r.Close() + } + b.StopTimer() +} + +func TestDelete2(t *testing.T) { + const N = 80000 + for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x314259} { + r := TreeNew(cmp) + set := r.Set + a := make([]int, N) + rng := rng() + for i := range a { + a[i] = (rng.Next() ^ x) << 1 + } + for _, k := range a { + set(k, 0) + } + + for i, k := range a { + ok := r.Delete(k) + if !ok { + t.Fatal(i, x, k) + } + + if g, e := r.Len(), N-i-1; g != e { + t.Fatal(i, g, e) + } + } + } +} + +func TestEnumeratorNext(t *testing.T) { + // seeking within 3 keys: 10, 20, 30 + table := []struct { + k int + hit bool + keys []int + }{ + {5, false, []int{10, 20, 30}}, + {10, true, []int{10, 20, 30}}, + {15, false, []int{20, 30}}, + {20, true, []int{20, 30}}, + {25, false, []int{30}}, + {30, true, []int{30}}, + {35, false, []int{}}, + } + + for i, test := range table { + up := test.keys + r := TreeNew(cmp) + + r.Set(10, 100) + r.Set(20, 200) + r.Set(30, 300) + + for verChange := 0; verChange < 16; verChange++ { + en, hit := r.Seek(test.k) + + if g, e := hit, test.hit; g != e { + t.Fatal(i, g, e) + } + + j := 0 + for { + if verChange&(1<= len(up) { + t.Fatal(i, j, verChange) + } + + if g, e := k, up[j]; g != e { + t.Fatal(i, j, verChange, g, e) + } + + if g, e := v, 10*up[j]; g != e { + t.Fatal(i, g, e) + } + + j++ + + } + + if g, e := j, len(up); g != e { + t.Fatal(i, j, g, e) + } + } + + } +} + +func TestEnumeratorPrev(t *testing.T) { + // seeking within 3 keys: 10, 20, 30 + table := []struct { + k int + hit bool + keys []int + }{ + {5, false, []int{10}}, + {10, true, []int{10}}, + {15, false, []int{20, 10}}, + {20, true, []int{20, 10}}, + {25, false, []int{30, 20, 10}}, + {30, true, []int{30, 20, 10}}, + {35, false, []int{}}, + } + + for i, test := range table { + dn := test.keys + r := TreeNew(cmp) + + r.Set(10, 100) + r.Set(20, 200) + r.Set(30, 300) + + for verChange := 0; verChange < 16; verChange++ { + en, hit := r.Seek(test.k) + + if g, e := hit, test.hit; g != e { + t.Fatal(i, g, e) + } + + j := 0 + for { + if verChange&(1<= len(dn) { + t.Fatal(i, j, verChange) + } + + if g, e := k, dn[j]; g != e { + t.Fatal(i, j, verChange, g, e) + } + + if g, e := v, 10*dn[j]; g != e { + t.Fatal(i, g, e) + } + + j++ + + } + + if g, e := j, len(dn); g != e { + t.Fatal(i, j, g, e) + } + } + + } +} + +func BenchmarkSeekSeq1e3(b *testing.B) { + benchmarkSeekSeq(b, 1e3) +} + +func BenchmarkSeekSeq1e4(b *testing.B) { + benchmarkSeekSeq(b, 1e4) +} + +func BenchmarkSeekSeq1e5(b *testing.B) { + benchmarkSeekSeq(b, 1e5) +} + +func BenchmarkSeekSeq1e6(b *testing.B) { + benchmarkSeekSeq(b, 1e6) +} + +func benchmarkSeekSeq(b *testing.B, n int) { + for i := 0; i < b.N; i++ { + b.StopTimer() + t := TreeNew(cmp) + for j := 0; j < n; j++ { + t.Set(j, 0) + } + debug.FreeOSMemory() + b.StartTimer() + for j := 0; j < n; j++ { + e, _ := t.Seek(j) + e.Close() + } + b.StopTimer() + t.Close() + } + b.StopTimer() +} + +func BenchmarkSeekRnd1e3(b *testing.B) { + benchmarkSeekRnd(b, 1e3) +} + +func BenchmarkSeekRnd1e4(b *testing.B) { + benchmarkSeekRnd(b, 1e4) +} + +func BenchmarkSeekRnd1e5(b *testing.B) { + benchmarkSeekRnd(b, 1e5) +} + +func BenchmarkSeekRnd1e6(b *testing.B) { + benchmarkSeekRnd(b, 1e6) +} + +func benchmarkSeekRnd(b *testing.B, n int) { + r := TreeNew(cmp) + rng := rng() + a := make([]int, n) + for i := range a { + a[i] = rng.Next() + } + for _, v := range a { + r.Set(v, 0) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, v := range a { + e, _ := r.Seek(v) + e.Close() + } + } + b.StopTimer() + r.Close() +} + +func BenchmarkNext1e3(b *testing.B) { + benchmarkNext(b, 1e3) +} + +func BenchmarkNext1e4(b *testing.B) { + benchmarkNext(b, 1e4) +} + +func BenchmarkNext1e5(b *testing.B) { + benchmarkNext(b, 1e5) +} + +func BenchmarkNext1e6(b *testing.B) { + benchmarkNext(b, 1e6) +} + +func benchmarkNext(b *testing.B, n int) { + t := TreeNew(cmp) + for i := 0; i < n; i++ { + t.Set(i, 0) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + en, err := t.SeekFirst() + if err != nil { + b.Fatal(err) + } + + m := 0 + for { + if _, _, err = en.Next(); err != nil { + break + } + m++ + } + if m != n { + b.Fatal(m) + } + } + b.StopTimer() + t.Close() +} + +func BenchmarkPrev1e3(b *testing.B) { + benchmarkPrev(b, 1e3) +} + +func BenchmarkPrev1e4(b *testing.B) { + benchmarkPrev(b, 1e4) +} + +func BenchmarkPrev1e5(b *testing.B) { + benchmarkPrev(b, 1e5) +} + +func BenchmarkPrev1e6(b *testing.B) { + benchmarkPrev(b, 1e6) +} + +func benchmarkPrev(b *testing.B, n int) { + t := TreeNew(cmp) + for i := 0; i < n; i++ { + t.Set(i, 0) + } + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + en, err := t.SeekLast() + if err != nil { + b.Fatal(err) + } + + m := 0 + for { + if _, _, err = en.Prev(); err != nil { + break + } + m++ + } + if m != n { + b.Fatal(m) + } + } + b.StopTimer() + t.Close() +} + +func TestSeekFirst0(t *testing.T) { + b := TreeNew(cmp) + _, err := b.SeekFirst() + if g, e := err, io.EOF; g != e { + t.Fatal(g, e) + } +} + +func TestSeekFirst1(t *testing.T) { + b := TreeNew(cmp) + b.Set(1, 10) + en, err := b.SeekFirst() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Next() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekFirst2(t *testing.T) { + b := TreeNew(cmp) + b.Set(1, 10) + b.Set(2, 20) + en, err := b.SeekFirst() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Next() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if k != 2 || v != 20 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekFirst3(t *testing.T) { + b := TreeNew(cmp) + b.Set(2, 20) + b.Set(3, 30) + b.Set(1, 10) + en, err := b.SeekFirst() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Next() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if k != 2 || v != 20 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if k != 3 || v != 30 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Next() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekLast0(t *testing.T) { + b := TreeNew(cmp) + _, err := b.SeekLast() + if g, e := err, io.EOF; g != e { + t.Fatal(g, e) + } +} + +func TestSeekLast1(t *testing.T) { + b := TreeNew(cmp) + b.Set(1, 10) + en, err := b.SeekLast() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Prev() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekLast2(t *testing.T) { + b := TreeNew(cmp) + b.Set(1, 10) + b.Set(2, 20) + en, err := b.SeekLast() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Prev() + if k != 2 || v != 20 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestSeekLast3(t *testing.T) { + b := TreeNew(cmp) + b.Set(2, 20) + b.Set(3, 30) + b.Set(1, 10) + en, err := b.SeekLast() + if err != nil { + t.Fatal(err) + } + + k, v, err := en.Prev() + if k != 3 || v != 30 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if k != 2 || v != 20 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if k != 1 || v != 10 || err != nil { + t.Fatal(k, v, err) + } + + k, v, err = en.Prev() + if err == nil { + t.Fatal(k, v, err) + } +} + +func TestPut(t *testing.T) { + tab := []struct { + pre []int // even index: K, odd index: V + newK int // Put(newK, ... + oldV int // Put()->oldV + exists bool // upd(exists) + write bool // upd()->write + post []int // even index: K, odd index: V + }{ + // 0 + { + []int{}, + 1, 0, false, false, + []int{}, + }, + { + []int{}, + 1, 0, false, true, + []int{1, -1}, + }, + { + []int{1, 10}, + 0, 0, false, false, + []int{1, 10}, + }, + { + []int{1, 10}, + 0, 0, false, true, + []int{0, -1, 1, 10}, + }, + { + []int{1, 10}, + 1, 10, true, false, + []int{1, 10}, + }, + + // 5 + { + []int{1, 10}, + 1, 10, true, true, + []int{1, -1}, + }, + { + []int{1, 10}, + 2, 0, false, false, + []int{1, 10}, + }, + { + []int{1, 10}, + 2, 0, false, true, + []int{1, 10, 2, -1}, + }, + } + + for iTest, test := range tab { + tr := TreeNew(cmp) + for i := 0; i < len(test.pre); i += 2 { + k, v := test.pre[i], test.pre[i+1] + tr.Set(k, v) + } + + oldV, written := tr.Put(test.newK, func(old int, exists bool) (newV int, write bool) { + if g, e := exists, test.exists; g != e { + t.Fatal(iTest, g, e) + } + + if exists { + if g, e := old, test.oldV; g != e { + t.Fatal(iTest, g, e) + } + } + return -1, test.write + }) + if test.exists { + if g, e := oldV, test.oldV; g != e { + t.Fatal(iTest, g, e) + } + } + + if g, e := written, test.write; g != e { + t.Fatal(iTest, g, e) + } + + n := len(test.post) + en, err := tr.SeekFirst() + if err != nil { + if n == 0 && err == io.EOF { + continue + } + + t.Fatal(iTest, err) + } + + for i := 0; i < len(test.post); i += 2 { + k, v, err := en.Next() + if err != nil { + t.Fatal(iTest, err) + } + + if g, e := k, test.post[i]; g != e { + t.Fatal(iTest, g, e) + } + + if g, e := v, test.post[i+1]; g != e { + t.Fatal(iTest, g, e) + } + } + + _, _, err = en.Next() + if g, e := err, io.EOF; g != e { + t.Fatal(iTest, g, e) + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/b/example/int.go b/Godeps/_workspace/src/github.com/cznic/b/example/int.go new file mode 100644 index 000000000..54f7fdffd --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/b/example/int.go @@ -0,0 +1,929 @@ +// Copyright 2014 The b Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import ( + "fmt" + "io" + "sync" +) + +const ( + kx = 32 //TODO benchmark tune this number if using custom key/value type(s). + kd = 32 //TODO benchmark tune this number if using custom key/value type(s). +) + +func init() { + if kd < 1 { + panic(fmt.Errorf("kd %d: out of range", kd)) + } + + if kx < 2 { + panic(fmt.Errorf("kx %d: out of range", kx)) + } +} + +var ( + btDPool = sync.Pool{New: func() interface{} { return &d{} }} + btEPool = btEpool{sync.Pool{New: func() interface{} { return &Enumerator{} }}} + btTPool = btTpool{sync.Pool{New: func() interface{} { return &Tree{} }}} + btXPool = sync.Pool{New: func() interface{} { return &x{} }} +) + +type btTpool struct{ sync.Pool } + +func (p *btTpool) get(cmp Cmp) *Tree { + x := p.Get().(*Tree) + x.cmp = cmp + return x +} + +type btEpool struct{ sync.Pool } + +func (p *btEpool) get(err error, hit bool, i int, k int, q *d, t *Tree, ver int64) *Enumerator { + x := p.Get().(*Enumerator) + x.err, x.hit, x.i, x.k, x.q, x.t, x.ver = err, hit, i, k, q, t, ver + return x +} + +type ( + // Cmp compares a and b. Return value is: + // + // < 0 if a < b + // 0 if a == b + // > 0 if a > b + // + Cmp func(a, b int) int + + d struct { // data page + c int + d [2*kd + 1]de + n *d + p *d + } + + de struct { // d element + k int + v int + } + + // Enumerator captures the state of enumerating a tree. It is returned + // from the Seek* methods. The enumerator is aware of any mutations + // made to the tree in the process of enumerating it and automatically + // resumes the enumeration at the proper key, if possible. + // + // However, once an Enumerator returns io.EOF to signal "no more + // items", it does no more attempt to "resync" on tree mutation(s). In + // other words, io.EOF from an Enumaretor is "sticky" (idempotent). + Enumerator struct { + err error + hit bool + i int + k int + q *d + t *Tree + ver int64 + } + + // Tree is a B+tree. + Tree struct { + c int + cmp Cmp + first *d + last *d + r interface{} + ver int64 + } + + xe struct { // x element + ch interface{} + k int + } + + x struct { // index page + c int + x [2*kx + 2]xe + } +) + +var ( // R/O zero values + zd d + zde de + ze Enumerator + zk int + zt Tree + zx x + zxe xe +) + +func clr(q interface{}) { + switch x := q.(type) { + case *x: + for i := 0; i <= x.c; i++ { // Ch0 Sep0 ... Chn-1 Sepn-1 Chn + clr(x.x[i].ch) + } + *x = zx + btXPool.Put(x) + case *d: + *x = zd + btDPool.Put(x) + } +} + +// -------------------------------------------------------------------------- x + +func newX(ch0 interface{}) *x { + r := btXPool.Get().(*x) + r.x[0].ch = ch0 + return r +} + +func (q *x) extract(i int) { + q.c-- + if i < q.c { + copy(q.x[i:], q.x[i+1:q.c+1]) + q.x[q.c].ch = q.x[q.c+1].ch + q.x[q.c].k = zk // GC + q.x[q.c+1] = zxe // GC + } +} + +func (q *x) insert(i int, k int, ch interface{}) *x { + c := q.c + if i < c { + q.x[c+1].ch = q.x[c].ch + copy(q.x[i+2:], q.x[i+1:c]) + q.x[i+1].k = q.x[i].k + } + c++ + q.c = c + q.x[i].k = k + q.x[i+1].ch = ch + return q +} + +func (q *x) siblings(i int) (l, r *d) { + if i >= 0 { + if i > 0 { + l = q.x[i-1].ch.(*d) + } + if i < q.c { + r = q.x[i+1].ch.(*d) + } + } + return +} + +// -------------------------------------------------------------------------- d + +func (l *d) mvL(r *d, c int) { + copy(l.d[l.c:], r.d[:c]) + copy(r.d[:], r.d[c:r.c]) + l.c += c + r.c -= c +} + +func (l *d) mvR(r *d, c int) { + copy(r.d[c:], r.d[:r.c]) + copy(r.d[:c], l.d[l.c-c:]) + r.c += c + l.c -= c +} + +// ----------------------------------------------------------------------- Tree + +// TreeNew returns a newly created, empty Tree. The compare function is used +// for key collation. +func TreeNew(cmp Cmp) *Tree { + return btTPool.get(cmp) +} + +// Clear removes all K/V pairs from the tree. +func (t *Tree) Clear() { + if t.r == nil { + return + } + + clr(t.r) + t.c, t.first, t.last, t.r = 0, nil, nil, nil + t.ver++ +} + +// Close performs Clear and recycles t to a pool for possible later reuse. No +// references to t should exist or such references must not be used afterwards. +func (t *Tree) Close() { + t.Clear() + *t = zt + btTPool.Put(t) +} + +func (t *Tree) cat(p *x, q, r *d, pi int) { + t.ver++ + q.mvL(r, r.c) + if r.n != nil { + r.n.p = q + } else { + t.last = q + } + q.n = r.n + *r = zd + btDPool.Put(r) + if p.c > 1 { + p.extract(pi) + p.x[pi].ch = q + return + } + + switch x := t.r.(type) { + case *x: + *x = zx + btXPool.Put(x) + case *d: + *x = zd + btDPool.Put(x) + } + t.r = q +} + +func (t *Tree) catX(p, q, r *x, pi int) { + t.ver++ + q.x[q.c].k = p.x[pi].k + copy(q.x[q.c+1:], r.x[:r.c]) + q.c += r.c + 1 + q.x[q.c].ch = r.x[r.c].ch + *r = zx + btXPool.Put(r) + if p.c > 1 { + p.c-- + pc := p.c + if pi < pc { + p.x[pi].k = p.x[pi+1].k + copy(p.x[pi+1:], p.x[pi+2:pc+1]) + p.x[pc].ch = p.x[pc+1].ch + p.x[pc].k = zk // GC + p.x[pc+1].ch = nil // GC + } + return + } + + switch x := t.r.(type) { + case *x: + *x = zx + btXPool.Put(x) + case *d: + *x = zd + btDPool.Put(x) + } + t.r = q +} + +// Delete removes the k's KV pair, if it exists, in which case Delete returns +// true. +func (t *Tree) Delete(k int) (ok bool) { + pi := -1 + var p *x + q := t.r + if q == nil { + return false + } + + for { + var i int + i, ok = t.find(q, k) + if ok { + switch x := q.(type) { + case *x: + if x.c < kx && q != t.r { + x, i = t.underflowX(p, x, pi, i) + } + pi = i + 1 + p = x + q = x.x[pi].ch + ok = false + continue + case *d: + t.extract(x, i) + if x.c >= kd { + return true + } + + if q != t.r { + t.underflow(p, x, pi) + } else if t.c == 0 { + t.Clear() + } + return true + } + } + + switch x := q.(type) { + case *x: + if x.c < kx && q != t.r { + x, i = t.underflowX(p, x, pi, i) + } + pi = i + p = x + q = x.x[i].ch + case *d: + return false + } + } +} + +func (t *Tree) extract(q *d, i int) { // (r int) { + t.ver++ + //r = q.d[i].v // prepared for Extract + q.c-- + if i < q.c { + copy(q.d[i:], q.d[i+1:q.c+1]) + } + q.d[q.c] = zde // GC + t.c-- + return +} + +func (t *Tree) find(q interface{}, k int) (i int, ok bool) { + var mk int + l := 0 + switch x := q.(type) { + case *x: + h := x.c - 1 + for l <= h { + m := (l + h) >> 1 + mk = x.x[m].k + switch cmp := t.cmp(k, mk); { + case cmp > 0: + l = m + 1 + case cmp == 0: + return m, true + default: + h = m - 1 + } + } + case *d: + h := x.c - 1 + for l <= h { + m := (l + h) >> 1 + mk = x.d[m].k + switch cmp := t.cmp(k, mk); { + case cmp > 0: + l = m + 1 + case cmp == 0: + return m, true + default: + h = m - 1 + } + } + } + return l, false +} + +// First returns the first item of the tree in the key collating order, or +// (zero-value, zero-value) if the tree is empty. +func (t *Tree) First() (k int, v int) { + if q := t.first; q != nil { + q := &q.d[0] + k, v = q.k, q.v + } + return +} + +// Get returns the value associated with k and true if it exists. Otherwise Get +// returns (zero-value, false). +func (t *Tree) Get(k int) (v int, ok bool) { + q := t.r + if q == nil { + return + } + + for { + var i int + if i, ok = t.find(q, k); ok { + switch x := q.(type) { + case *x: + q = x.x[i+1].ch + continue + case *d: + return x.d[i].v, true + } + } + switch x := q.(type) { + case *x: + q = x.x[i].ch + default: + return + } + } +} + +func (t *Tree) insert(q *d, i int, k int, v int) *d { + t.ver++ + c := q.c + if i < c { + copy(q.d[i+1:], q.d[i:c]) + } + c++ + q.c = c + q.d[i].k, q.d[i].v = k, v + t.c++ + return q +} + +// Last returns the last item of the tree in the key collating order, or +// (zero-value, zero-value) if the tree is empty. +func (t *Tree) Last() (k int, v int) { + if q := t.last; q != nil { + q := &q.d[q.c-1] + k, v = q.k, q.v + } + return +} + +// Len returns the number of items in the tree. +func (t *Tree) Len() int { + return t.c +} + +func (t *Tree) overflow(p *x, q *d, pi, i int, k int, v int) { + t.ver++ + l, r := p.siblings(pi) + + if l != nil && l.c < 2*kd { + l.mvL(q, 1) + t.insert(q, i-1, k, v) + p.x[pi-1].k = q.d[0].k + return + } + + if r != nil && r.c < 2*kd { + if i < 2*kd { + q.mvR(r, 1) + t.insert(q, i, k, v) + p.x[pi].k = r.d[0].k + return + } + + t.insert(r, 0, k, v) + p.x[pi].k = k + return + } + + t.split(p, q, pi, i, k, v) +} + +// Seek returns an Enumerator positioned on a an item such that k >= item's +// key. ok reports if k == item.key The Enumerator's position is possibly +// after the last item in the tree. +func (t *Tree) Seek(k int) (e *Enumerator, ok bool) { + q := t.r + if q == nil { + e = btEPool.get(nil, false, 0, k, nil, t, t.ver) + return + } + + for { + var i int + if i, ok = t.find(q, k); ok { + switch x := q.(type) { + case *x: + q = x.x[i+1].ch + continue + case *d: + return btEPool.get(nil, ok, i, k, x, t, t.ver), true + } + } + + switch x := q.(type) { + case *x: + q = x.x[i].ch + case *d: + return btEPool.get(nil, ok, i, k, x, t, t.ver), false + } + } +} + +// SeekFirst returns an enumerator positioned on the first KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returned and e will be nil. +func (t *Tree) SeekFirst() (e *Enumerator, err error) { + q := t.first + if q == nil { + return nil, io.EOF + } + + return btEPool.get(nil, true, 0, q.d[0].k, q, t, t.ver), nil +} + +// SeekLast returns an enumerator positioned on the last KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returned and e will be nil. +func (t *Tree) SeekLast() (e *Enumerator, err error) { + q := t.last + if q == nil { + return nil, io.EOF + } + + return btEPool.get(nil, true, q.c-1, q.d[q.c-1].k, q, t, t.ver), nil +} + +// Set sets the value associated with k. +func (t *Tree) Set(k int, v int) { + //dbg("--- PRE Set(%v, %v)\n%s", k, v, t.dump()) + //defer func() { + // dbg("--- POST\n%s\n====\n", t.dump()) + //}() + + pi := -1 + var p *x + q := t.r + if q == nil { + z := t.insert(btDPool.Get().(*d), 0, k, v) + t.r, t.first, t.last = z, z, z + return + } + + for { + i, ok := t.find(q, k) + if ok { + switch x := q.(type) { + case *x: + if x.c > 2*kx { + x, i = t.splitX(p, x, pi, i) + } + pi = i + 1 + p = x + q = x.x[i+1].ch + continue + case *d: + x.d[i].v = v + } + return + } + + switch x := q.(type) { + case *x: + if x.c > 2*kx { + x, i = t.splitX(p, x, pi, i) + } + pi = i + p = x + q = x.x[i].ch + case *d: + switch { + case x.c < 2*kd: + t.insert(x, i, k, v) + default: + t.overflow(p, x, pi, i, k, v) + } + return + } + } +} + +// Put combines Get and Set in a more efficient way where the tree is walked +// only once. The upd(ater) receives (old-value, true) if a KV pair for k +// exists or (zero-value, false) otherwise. It can then return a (new-value, +// true) to create or overwrite the existing value in the KV pair, or +// (whatever, false) if it decides not to create or not to update the value of +// the KV pair. +// +// tree.Set(k, v) call conceptually equals calling +// +// tree.Put(k, func(int, bool){ return v, true }) +// +// modulo the differing return values. +func (t *Tree) Put(k int, upd func(oldV int, exists bool) (newV int, write bool)) (oldV int, written bool) { + pi := -1 + var p *x + q := t.r + var newV int + if q == nil { + // new KV pair in empty tree + newV, written = upd(newV, false) + if !written { + return + } + + z := t.insert(btDPool.Get().(*d), 0, k, newV) + t.r, t.first, t.last = z, z, z + return + } + + for { + i, ok := t.find(q, k) + if ok { + switch x := q.(type) { + case *x: + if x.c > 2*kx { + x, i = t.splitX(p, x, pi, i) + } + pi = i + 1 + p = x + q = x.x[i+1].ch + continue + case *d: + oldV = x.d[i].v + newV, written = upd(oldV, true) + if !written { + return + } + + x.d[i].v = newV + } + return + } + + switch x := q.(type) { + case *x: + if x.c > 2*kx { + x, i = t.splitX(p, x, pi, i) + } + pi = i + p = x + q = x.x[i].ch + case *d: // new KV pair + newV, written = upd(newV, false) + if !written { + return + } + + switch { + case x.c < 2*kd: + t.insert(x, i, k, newV) + default: + t.overflow(p, x, pi, i, k, newV) + } + return + } + } +} + +func (t *Tree) split(p *x, q *d, pi, i int, k int, v int) { + t.ver++ + r := btDPool.Get().(*d) + if q.n != nil { + r.n = q.n + r.n.p = r + } else { + t.last = r + } + q.n = r + r.p = q + + copy(r.d[:], q.d[kd:2*kd]) + for i := range q.d[kd:] { + q.d[kd+i] = zde + } + q.c = kd + r.c = kd + var done bool + if i > kd { + done = true + t.insert(r, i-kd, k, v) + } + if pi >= 0 { + p.insert(pi, r.d[0].k, r) + } else { + t.r = newX(q).insert(0, r.d[0].k, r) + } + if done { + return + } + + t.insert(q, i, k, v) +} + +func (t *Tree) splitX(p *x, q *x, pi int, i int) (*x, int) { + t.ver++ + r := btXPool.Get().(*x) + copy(r.x[:], q.x[kx+1:]) + q.c = kx + r.c = kx + if pi >= 0 { + p.insert(pi, q.x[kx].k, r) + q.x[kx].k = zk + for i := range q.x[kx+1:] { + q.x[kx+i+1] = zxe + } + + switch { + case i < kx: + return q, i + case i == kx: + return p, pi + default: // i > kx + return r, i - kx - 1 + } + } + + nr := newX(q).insert(0, q.x[kx].k, r) + t.r = nr + q.x[kx].k = zk + for i := range q.x[kx+1:] { + q.x[kx+i+1] = zxe + } + + switch { + case i < kx: + return q, i + case i == kx: + return nr, 0 + default: // i > kx + return r, i - kx - 1 + } +} + +func (t *Tree) underflow(p *x, q *d, pi int) { + t.ver++ + l, r := p.siblings(pi) + + if l != nil && l.c+q.c >= 2*kd { + l.mvR(q, 1) + p.x[pi-1].k = q.d[0].k + return + } + + if r != nil && q.c+r.c >= 2*kd { + q.mvL(r, 1) + p.x[pi].k = r.d[0].k + r.d[r.c] = zde // GC + return + } + + if l != nil { + t.cat(p, l, q, pi-1) + return + } + + t.cat(p, q, r, pi) +} + +func (t *Tree) underflowX(p *x, q *x, pi int, i int) (*x, int) { + t.ver++ + var l, r *x + + if pi >= 0 { + if pi > 0 { + l = p.x[pi-1].ch.(*x) + } + if pi < p.c { + r = p.x[pi+1].ch.(*x) + } + } + + if l != nil && l.c > kx { + q.x[q.c+1].ch = q.x[q.c].ch + copy(q.x[1:], q.x[:q.c]) + q.x[0].ch = l.x[l.c].ch + q.x[0].k = p.x[pi-1].k + q.c++ + i++ + l.c-- + p.x[pi-1].k = l.x[l.c].k + return q, i + } + + if r != nil && r.c > kx { + q.x[q.c].k = p.x[pi].k + q.c++ + q.x[q.c].ch = r.x[0].ch + p.x[pi].k = r.x[0].k + copy(r.x[:], r.x[1:r.c]) + r.c-- + rc := r.c + r.x[rc].ch = r.x[rc+1].ch + r.x[rc].k = zk + r.x[rc+1].ch = nil + return q, i + } + + if l != nil { + i += l.c + 1 + t.catX(p, l, q, pi-1) + q = l + return q, i + } + + t.catX(p, q, r, pi) + return q, i +} + +// ----------------------------------------------------------------- Enumerator + +// Close recycles e to a pool for possible later reuse. No references to e +// should exist or such references must not be used afterwards. +func (e *Enumerator) Close() { + *e = ze + btEPool.Put(e) +} + +// Next returns the currently enumerated item, if it exists and moves to the +// next item in the key collation order. If there is no item to return, err == +// io.EOF is returned. +func (e *Enumerator) Next() (k int, v int, err error) { + if err = e.err; err != nil { + return + } + + if e.ver != e.t.ver { + f, hit := e.t.Seek(e.k) + if !e.hit && hit { + if err = f.next(); err != nil { + return + } + } + + *e = *f + f.Close() + } + if e.q == nil { + e.err, err = io.EOF, io.EOF + return + } + + if e.i >= e.q.c { + if err = e.next(); err != nil { + return + } + } + + i := e.q.d[e.i] + k, v = i.k, i.v + e.k, e.hit = k, false + e.next() + return +} + +func (e *Enumerator) next() error { + if e.q == nil { + e.err = io.EOF + return io.EOF + } + + switch { + case e.i < e.q.c-1: + e.i++ + default: + if e.q, e.i = e.q.n, 0; e.q == nil { + e.err = io.EOF + } + } + return e.err +} + +// Prev returns the currently enumerated item, if it exists and moves to the +// previous item in the key collation order. If there is no item to return, err +// == io.EOF is returned. +func (e *Enumerator) Prev() (k int, v int, err error) { + if err = e.err; err != nil { + return + } + + if e.ver != e.t.ver { + f, hit := e.t.Seek(e.k) + if !e.hit && hit { + if err = f.prev(); err != nil { + return + } + } + + *e = *f + f.Close() + } + if e.q == nil { + e.err, err = io.EOF, io.EOF + return + } + + if e.i >= e.q.c { + if err = e.next(); err != nil { + return + } + } + + i := e.q.d[e.i] + k, v = i.k, i.v + e.k, e.hit = k, false + e.prev() + return +} + +func (e *Enumerator) prev() error { + if e.q == nil { + e.err = io.EOF + return io.EOF + } + + switch { + case e.i > 0: + e.i-- + default: + if e.q = e.q.p; e.q == nil { + e.err = io.EOF + break + } + + e.i = e.q.c - 1 + } + return e.err +} diff --git a/Godeps/_workspace/src/github.com/cznic/bufs/AUTHORS b/Godeps/_workspace/src/github.com/cznic/bufs/AUTHORS new file mode 100644 index 000000000..0078f5f5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/bufs/AUTHORS @@ -0,0 +1,11 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/bufs/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/bufs/CONTRIBUTORS new file mode 100644 index 000000000..5e86f0607 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/bufs/CONTRIBUTORS @@ -0,0 +1,9 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/bufs/LICENSE b/Godeps/_workspace/src/github.com/cznic/bufs/LICENSE new file mode 100644 index 000000000..7d80fe28e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/bufs/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The bufs Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/bufs/Makefile b/Godeps/_workspace/src/github.com/cznic/bufs/Makefile new file mode 100644 index 000000000..c98c23d6d --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/bufs/Makefile @@ -0,0 +1,31 @@ +# Copyright 2014 The bufs Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +all: clean + go fmt + go test -i + go test + go build + go vet + golint . + go install + make todo + +todo: + @grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alnum:]] *.go || true + @grep -n TODO *.go || true + @grep -n FIXME *.go || true + @grep -n BUG *.go || true + +clean: + rm -f bufs.test mem.out *~ + +demo: + go test -bench . -benchmem + go test -c + ./bufs.test -test.v -test.run Foo -test.memprofile mem.out \ + -test.memprofilerate 1 + go tool pprof bufs.test mem.out --alloc_space --nodefraction 0.0001 \ + --edgefraction 0 -web + @echo "Note: Foo vs FooBufs allocated memory is in hundreds of MBs vs 8 kB." diff --git a/Godeps/_workspace/src/github.com/cznic/bufs/README.md b/Godeps/_workspace/src/github.com/cznic/bufs/README.md new file mode 100644 index 000000000..3f3d56f19 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/bufs/README.md @@ -0,0 +1,8 @@ +bufs +==== + +Package bufs implements a simple buffer cache. + + installation: go get github.com/cznic/bufs + +documentation: http://godoc.org/github.com/cznic/bufs diff --git a/Godeps/_workspace/src/github.com/cznic/bufs/bufs.go b/Godeps/_workspace/src/github.com/cznic/bufs/bufs.go new file mode 100644 index 000000000..f4e0eee2a --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/bufs/bufs.go @@ -0,0 +1,391 @@ +// Copyright 2014 The bufs Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bufs implements a simple buffer cache. +// +// The intended use scheme is like: +// +// type Foo struct { +// buffers bufs.Buffers +// ... +// } +// +// // Bar can call Qux, but not the other way around (in this example). +// const maxFooDepth = 2 +// +// func NewFoo() *Foo { +// return &Foo{buffers: bufs.New(maxFooDepth), ...} +// } +// +// func (f *Foo) Bar(n int) { +// buf := f.buffers.Alloc(n) // needed locally for computation and/or I/O +// defer f.buffers.Free() +// ... +// f.Qux(whatever) +// } +// +// func (f *Foo) Qux(n int) { +// buf := f.buffers.Alloc(n) // needed locally for computation and/or I/O +// defer f.buffers.Free() +// ... +// } +// +// The whole idea behind 'bufs' is that when calling e.g. Foo.Bar N times, then +// normally, without using 'bufs', there will be 2*N (in this example) []byte +// buffers allocated. While using 'bufs', only 2 buffers (in this example) +// will ever be created. For large N it can be a substantial difference. +// +// It's not a good idea to use Buffers to cache too big buffers. The cost of +// having a cached buffer is that the buffer is naturally not eligible for +// garbage collection. Of course, that holds only while the Foo instance is +// reachable, in the above example. +// +// The buffer count limit is intentionally "hard" (read panicking), although +// configurable in New(). The rationale is to prevent recursive calls, using +// Alloc, to cause excessive, "static" memory consumption. Tune the limit +// carefully or do not use Buffers from within [mutually] recursive functions +// where the nesting depth is not realistically bounded to some rather small +// number. +// +// Buffers cannot guarantee improvements to you program performance. There may +// be a gain in case where they fit well. Firm grasp on what your code is +// actually doing, when and in what order is essential to proper use of +// Buffers. It's _highly_ recommended to first do profiling and memory +// profiling before even thinking about using 'bufs'. The real world example, +// and cause for this package, was a first correct, yet no optimizations done +// version of a program; producing few MB of useful data while allocating 20+GB +// of memory. Of course the garbage collector properly kicked in, yet the +// memory abuse caused ~80+% of run time to be spent memory management. The +// program _was_ expected to be slow in its still development phase, but the +// bottleneck was guessed to be in I/O. Actually the hard disk was waiting for +// the billions bytes being allocated and zeroed. Garbage collect on low +// memory, rinse and repeat. +// +// In the provided tests, TestFoo and TestFooBufs do the same simulated work, +// except the later uses Buffers while the former does not. Suggested test runs +// which show the differences: +// +// $ go test -bench . -benchmem +// +// or +// +// $ go test -c +// $ ./bufs.test -test.v -test.run Foo -test.memprofile mem.out -test.memprofilerate 1 +// $ go tool pprof bufs.test mem.out --alloc_space --nodefraction 0.0001 --edgefraction 0 -web +// $ # Note: Foo vs FooBufs allocated memory is in hundreds of MBs vs 8 kB. +// +// or +// +// $ make demo # same as all of the above +// +// +// NOTE: Alloc/Free calls must be properly nested in the same way as in for +// example BeginTransaction/EndTransaction pairs. If your code can panic then +// the pairing should be enforced by deferred calls. +// +// NOTE: Buffers objects do not allocate any space until requested by Alloc, +// the mechanism works on demand only. +// +// FAQ: Why the 'bufs' package name? +// +// Package name 'bufs' was intentionally chosen instead of the perhaps more +// conventional 'buf'. There are already too many 'buf' named things in the +// code out there and that'll be a source of a lot of trouble. It's a bit +// similar situation as in the case of package "strings" (not "string"). +package bufs + +import ( + "errors" + "sort" + "sync" +) + +// Buffers type represents a buffer ([]byte) cache. +// +// NOTE: Do not modify Buffers directly, use only its methods. Do not create +// additional values (copies) of Buffers, that'll break its functionality. Use +// a pointer instead to refer to a single instance from different +// places/scopes. +type Buffers [][]byte + +// New returns a newly created instance of Buffers with a maximum capacity of n +// buffers. +// +// NOTE: 'bufs.New(n)' is the same as 'make(bufs.Buffers, n)'. +func New(n int) Buffers { + return make(Buffers, n) +} + +// Alloc will return a buffer such that len(r) == n. It will firstly try to +// find an existing and unused buffer of big enough size. Only when there is no +// such, then one of the buffer slots is reallocated to a bigger size. +// +// It's okay to use append with buffers returned by Alloc. But it can cause +// allocation in that case and will again be producing load for the garbage +// collector. The best use of Alloc is for I/O buffers where the needed size of +// the buffer is figured out at some point of the code path in a 'final size' +// sense. Another real world example are compression/decompression buffers. +// +// NOTE: The buffer returned by Alloc _is not_ zeroed. That's okay for e.g. +// passing a buffer to io.Reader. If you need a zeroed buffer use Calloc. +// +// NOTE: Buffers returned from Alloc _must not_ be exposed/returned to your +// clients. Those buffers are intended to be used strictly internally, within +// the methods of some "object". +// +// NOTE: Alloc will panic if there are no buffers (buffer slots) left. +func (p *Buffers) Alloc(n int) (r []byte) { + b := *p + if len(b) == 0 { + panic(errors.New("Buffers.Alloc: out of buffers")) + } + + biggest, best, biggestI, bestI := -1, -1, -1, -1 + for i, v := range b { + //ln := len(v) + // The above was correct, buts it's just confusing. It worked + // because not the buffers, but slices of them are returned in + // the 'if best >= n' code path. + ln := cap(v) + + if ln >= biggest { + biggest, biggestI = ln, i + } + + if ln >= n && (bestI < 0 || best > ln) { + best, bestI = ln, i + if ln == n { + break + } + } + } + + last := len(b) - 1 + if best >= n { + r = b[bestI] + b[last], b[bestI] = b[bestI], b[last] + *p = b[:last] + return r[:n] + } + + r = make([]byte, n, overCommit(n)) + b[biggestI] = r + b[last], b[biggestI] = b[biggestI], b[last] + *p = b[:last] + return +} + +// Calloc will acquire a buffer using Alloc and then clears it to zeros. The +// zeroing goes up to n, not cap(r). +func (p *Buffers) Calloc(n int) (r []byte) { + r = p.Alloc(n) + for i := range r { + r[i] = 0 + } + return +} + +// Free makes the lastly allocated by Alloc buffer free (available) again for +// Alloc. +// +// NOTE: Improper Free invocations, like in the sequence {New, Alloc, Free, +// Free}, will panic. +func (p *Buffers) Free() { + b := *p + b = b[:len(b)+1] + *p = b +} + +// Stats reports memory consumed by Buffers, without accounting for some +// (smallish) additional overhead. +func (p *Buffers) Stats() (bytes int) { + b := *p + b = b[:cap(b)] + for _, v := range b { + bytes += cap(v) + } + return +} + +// Cache caches buffers ([]byte). A zero value of Cache is ready for use. +// +// NOTE: Do not modify a Cache directly, use only its methods. Do not create +// additional values (copies) of a Cache, that'll break its functionality. Use +// a pointer instead to refer to a single instance from different +// places/scopes. +type Cache [][]byte + +// Get returns a buffer ([]byte) of length n. If no such buffer is cached then +// a biggest cached buffer is resized to have length n and returned. If there +// are no cached items at all, Get returns a newly allocated buffer. +// +// In other words the cache policy is: +// +// - If the cache is empty, the buffer must be newly created and returned. +// Cache remains empty. +// +// - If a buffer of sufficient size is found in the cache, remove it from the +// cache and return it. +// +// - Otherwise the cache is non empty, but no cached buffer is big enough. +// Enlarge the biggest cached buffer, remove it from the cache and return it. +// This provide cached buffers size adjustment based on demand. +// +// In short, if the cache is not empty, Get guarantees to make it always one +// item less. This rules prevent uncontrolled cache grow in some scenarios. +// The older policy was not preventing that. Another advantage is better cached +// buffers sizes "auto tuning", although not in every possible use case. +// +// NOTE: The buffer returned by Get _is not guaranteed_ to be zeroed. That's +// okay for e.g. passing a buffer to io.Reader. If you need a zeroed buffer +// use Cget. +func (c *Cache) Get(n int) []byte { + r, _ := c.get(n) + return r +} + +func (c *Cache) get(n int) (r []byte, isZeroed bool) { + s := *c + lens := len(s) + if lens == 0 { + r, isZeroed = make([]byte, n, overCommit(n)), true + return + } + + i := sort.Search(lens, func(x int) bool { return len(s[x]) >= n }) + if i == lens { + i-- + s[i] = make([]byte, n, overCommit(n)) + } + r = s[i][:n] + copy(s[i:], s[i+1:]) + s[lens-1] = nil + s = s[:lens-1] + *c = s + return r, false +} + +// Cget will acquire a buffer using Get and then clears it to zeros. The +// zeroing goes up to n, not cap(r). +func (c *Cache) Cget(n int) (r []byte) { + r, ok := c.get(n) + if ok { + return + } + + for i := range r { + r[i] = 0 + } + return +} + +// Put caches b for possible later reuse (via Get). No other references to b's +// backing array may exist. Otherwise a big mess is sooner or later inevitable. +func (c *Cache) Put(b []byte) { + b = b[:cap(b)] + lenb := len(b) + if lenb == 0 { + return + } + + s := *c + lens := len(s) + i := sort.Search(lens, func(x int) bool { return len(s[x]) >= lenb }) + s = append(s, nil) + copy(s[i+1:], s[i:]) + s[i] = b + *c = s + return +} + +// Stats reports memory consumed by a Cache, without accounting for some +// (smallish) additional overhead. 'n' is the number of cached buffers, bytes +// is their combined capacity. +func (c Cache) Stats() (n, bytes int) { + n = len(c) + for _, v := range c { + bytes += cap(v) + } + return +} + +// CCache is a Cache which is safe for concurrent use by multiple goroutines. +type CCache struct { + c Cache + mu sync.Mutex +} + +// Get returns a buffer ([]byte) of length n. If no such buffer is cached then +// a biggest cached buffer is resized to have length n and returned. If there +// are no cached items at all, Get returns a newly allocated buffer. +// +// In other words the cache policy is: +// +// - If the cache is empty, the buffer must be newly created and returned. +// Cache remains empty. +// +// - If a buffer of sufficient size is found in the cache, remove it from the +// cache and return it. +// +// - Otherwise the cache is non empty, but no cached buffer is big enough. +// Enlarge the biggest cached buffer, remove it from the cache and return it. +// This provide cached buffers size adjustment based on demand. +// +// In short, if the cache is not empty, Get guarantees to make it always one +// item less. This rules prevent uncontrolled cache grow in some scenarios. +// The older policy was not preventing that. Another advantage is better cached +// buffers sizes "auto tuning", although not in every possible use case. +// +// NOTE: The buffer returned by Get _is not guaranteed_ to be zeroed. That's +// okay for e.g. passing a buffer to io.Reader. If you need a zeroed buffer +// use Cget. +func (c *CCache) Get(n int) []byte { + c.mu.Lock() + r, _ := c.c.get(n) + c.mu.Unlock() + return r +} + +// Cget will acquire a buffer using Get and then clears it to zeros. The +// zeroing goes up to n, not cap(r). +func (c *CCache) Cget(n int) (r []byte) { + c.mu.Lock() + r = c.c.Cget(n) + c.mu.Unlock() + return +} + +// Put caches b for possible later reuse (via Get). No other references to b's +// backing array may exist. Otherwise a big mess is sooner or later inevitable. +func (c *CCache) Put(b []byte) { + c.mu.Lock() + c.c.Put(b) + c.mu.Unlock() +} + +// Stats reports memory consumed by a Cache, without accounting for some +// (smallish) additional overhead. 'n' is the number of cached buffers, bytes +// is their combined capacity. +func (c *CCache) Stats() (n, bytes int) { + c.mu.Lock() + n, bytes = c.c.Stats() + c.mu.Unlock() + return +} + +// GCache is a ready to use global instance of a CCache. +var GCache CCache + +func overCommit(n int) int { + switch { + case n < 8: + return 8 + case n < 1e5: + return 2 * n + case n < 1e6: + return 3 * n / 2 + default: + return n + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/bufs/bufs_test.go b/Godeps/_workspace/src/github.com/cznic/bufs/bufs_test.go new file mode 100644 index 000000000..62400b006 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/bufs/bufs_test.go @@ -0,0 +1,174 @@ +// Copyright 2014 The bufs Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bufs + +import ( + "fmt" + "path" + "runtime" + "testing" +) + +var dbg = func(s string, va ...interface{}) { + _, fn, fl, _ := runtime.Caller(1) + fmt.Printf("%s:%d: ", path.Base(fn), fl) + fmt.Printf(s, va...) + fmt.Println() +} + +func Test0(t *testing.T) { + b := New(0) + defer func() { + recover() + }() + + b.Alloc(1) + t.Fatal("unexpected success") +} + +func Test1(t *testing.T) { + b := New(1) + expected := false + defer func() { + if e := recover(); e != nil && !expected { + t.Fatal(fmt.Errorf("%v", e)) + } + }() + + b.Alloc(1) + expected = true + b.Alloc(1) + t.Fatal("unexpected success") +} + +func Test2(t *testing.T) { + b := New(1) + expected := false + defer func() { + if e := recover(); e != nil && !expected { + t.Fatal(fmt.Errorf("%v", e)) + } + }() + + b.Alloc(1) + b.Free() + b.Alloc(1) + expected = true + b.Alloc(1) + t.Fatal("unexpected success") +} + +func Test3(t *testing.T) { + b := New(1) + expected := false + defer func() { + if e := recover(); e != nil && !expected { + t.Fatal(fmt.Errorf("%v", e)) + } + }() + + b.Alloc(1) + b.Free() + expected = true + b.Free() + t.Fatal("unexpected success") +} + +const ( + N = 1e5 + bufSize = 1 << 12 +) + +type Foo struct { + result []byte +} + +func NewFoo() *Foo { + return &Foo{} +} + +func (f *Foo) Bar(n int) { + buf := make([]byte, n) + sum := 0 + for _, v := range buf { + sum += int(v) + } + f.result = append(f.result, byte(sum)) + f.Qux(n) +} + +func (f *Foo) Qux(n int) { + buf := make([]byte, n) + sum := 0 + for _, v := range buf { + sum += int(v) + } + f.result = append(f.result, byte(sum)) +} + +type FooBufs struct { + buffers Buffers + result []byte +} + +const maxFooDepth = 2 + +func NewFooBufs() *FooBufs { + return &FooBufs{buffers: New(maxFooDepth)} +} + +func (f *FooBufs) Bar(n int) { + buf := f.buffers.Alloc(n) + defer f.buffers.Free() + + sum := 0 + for _, v := range buf { + sum += int(v) + } + f.result = append(f.result, byte(sum)) + f.Qux(n) +} + +func (f *FooBufs) Qux(n int) { + buf := f.buffers.Alloc(n) + defer f.buffers.Free() + + sum := 0 + for _, v := range buf { + sum += int(v) + } + f.result = append(f.result, byte(sum)) +} + +func TestFoo(t *testing.T) { + foo := NewFoo() + for i := 0; i < N; i++ { + foo.Bar(bufSize) + } +} + +func TestFooBufs(t *testing.T) { + foo := NewFooBufs() + for i := 0; i < N; i++ { + foo.Bar(bufSize) + } + t.Log("buffers.Stats()", foo.buffers.Stats()) +} + +func BenchmarkFoo(b *testing.B) { + b.SetBytes(2 * bufSize) + foo := NewFoo() + for i := 0; i < b.N; i++ { + foo.Bar(bufSize) + } +} + +func BenchmarkFooBufs(b *testing.B) { + b.SetBytes(2 * bufSize) + foo := NewFooBufs() + for i := 0; i < b.N; i++ { + foo.Bar(bufSize) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/2pc.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/2pc.go new file mode 100644 index 000000000..887604def --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/2pc.go @@ -0,0 +1,324 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Two Phase Commit & Structural ACID + +package lldb + +import ( + "bufio" + "encoding/binary" + "fmt" + "io" + "os" + + "github.com/cznic/fileutil" + "github.com/cznic/mathutil" +) + +var _ Filer = &ACIDFiler0{} // Ensure ACIDFiler0 is a Filer + +type acidWrite struct { + b []byte + off int64 +} + +type acidWriter0 ACIDFiler0 + +func (a *acidWriter0) WriteAt(b []byte, off int64) (n int, err error) { + f := (*ACIDFiler0)(a) + if f.bwal == nil { // new epoch + f.data = f.data[:0] + f.bwal = bufio.NewWriter(f.wal) + if err = a.writePacket([]interface{}{wpt00Header, walTypeACIDFiler0, ""}); err != nil { + return + } + } + + if err = a.writePacket([]interface{}{wpt00WriteData, b, off}); err != nil { + return + } + + f.data = append(f.data, acidWrite{b, off}) + return len(b), nil +} + +func (a *acidWriter0) writePacket(items []interface{}) (err error) { + f := (*ACIDFiler0)(a) + b, err := EncodeScalars(items...) + if err != nil { + return + } + + var b4 [4]byte + binary.BigEndian.PutUint32(b4[:], uint32(len(b))) + if _, err = f.bwal.Write(b4[:]); err != nil { + return + } + + if _, err = f.bwal.Write(b); err != nil { + return + } + + if m := (4 + len(b)) % 16; m != 0 { + var pad [15]byte + _, err = f.bwal.Write(pad[:16-m]) + } + return +} + +// WAL Packet Tags +const ( + wpt00Header = iota + wpt00WriteData + wpt00Checkpoint +) + +const ( + walTypeACIDFiler0 = iota +) + +// ACIDFiler0 is a very simple, synchronous implementation of 2PC. It uses a +// single write ahead log file to provide the structural atomicity +// (BeginUpdate/EndUpdate/Rollback) and durability (DB can be recovered from +// WAL if a crash occurred). +// +// ACIDFiler0 is a Filer. +// +// NOTE: Durable synchronous 2PC involves three fsyncs in this implementation +// (WAL, DB, zero truncated WAL). Where possible, it's recommended to collect +// transactions for, say one second before performing the two phase commit as +// the typical performance for rotational hard disks is about few tens of +// fsyncs per second atmost. For an example of such collective transaction +// approach please see the colecting FSM STT in Dbm's documentation[1]. +// +// [1]: http://godoc.org/github.com/cznic/exp/dbm +type ACIDFiler0 struct { + *RollbackFiler + wal *os.File + bwal *bufio.Writer + data []acidWrite + testHook bool // keeps WAL untruncated (once) + peakWal int64 // tracks WAL maximum used size + peakBitFilerPages int // track maximum transaction memory +} + +// NewACIDFiler0 returns a newly created ACIDFiler0 with WAL in wal. +// +// If the WAL is zero sized then a previous clean shutdown of db is taken for +// granted and no recovery procedure is taken. +// +// If the WAL is of non zero size then it is checked for having a +// commited/fully finished transaction not yet been reflected in db. If such +// transaction exists it's committed to db. If the recovery process finishes +// successfully, the WAL is truncated to zero size and fsync'ed prior to return +// from NewACIDFiler0. +func NewACIDFiler(db Filer, wal *os.File) (r *ACIDFiler0, err error) { + fi, err := wal.Stat() + if err != nil { + return + } + + r = &ACIDFiler0{wal: wal} + + if fi.Size() != 0 { + if err = r.recoverDb(db); err != nil { + return + } + } + + acidWriter := (*acidWriter0)(r) + + if r.RollbackFiler, err = NewRollbackFiler( + db, + func(sz int64) (err error) { + // Checkpoint + if err = acidWriter.writePacket([]interface{}{wpt00Checkpoint, sz}); err != nil { + return + } + + if err = r.bwal.Flush(); err != nil { + return + } + + r.bwal = nil + + if err = r.wal.Sync(); err != nil { + return + } + + wfi, err := r.wal.Stat() + switch err != nil { + case true: + // unexpected, but ignored + case false: + r.peakWal = mathutil.MaxInt64(wfi.Size(), r.peakWal) + } + + // Phase 1 commit complete + + for _, v := range r.data { + if _, err := db.WriteAt(v.b, v.off); err != nil { + return err + } + } + + if err = db.Truncate(sz); err != nil { + return + } + + if err = db.Sync(); err != nil { + return + } + + // Phase 2 commit complete + + if !r.testHook { + if err = r.wal.Truncate(0); err != nil { + return + } + + if _, err = r.wal.Seek(0, 0); err != nil { + return + } + } + + r.testHook = false + return r.wal.Sync() + + }, + acidWriter, + ); err != nil { + return + } + + return r, nil +} + +// PeakWALSize reports the maximum size WAL has ever used. +func (a ACIDFiler0) PeakWALSize() int64 { + return a.peakWal +} + +func (a *ACIDFiler0) readPacket(f *bufio.Reader) (items []interface{}, err error) { + var b4 [4]byte + n, err := io.ReadAtLeast(f, b4[:], 4) + if n != 4 { + return + } + + ln := int(binary.BigEndian.Uint32(b4[:])) + m := (4 + ln) % 16 + padd := (16 - m) % 16 + b := make([]byte, ln+padd) + if n, err = io.ReadAtLeast(f, b, len(b)); n != len(b) { + return + } + + return DecodeScalars(b[:ln]) +} + +func (a *ACIDFiler0) recoverDb(db Filer) (err error) { + fi, err := a.wal.Stat() + if err != nil { + return &ErrILSEQ{Type: ErrInvalidWAL, Name: a.wal.Name(), More: err} + } + + if sz := fi.Size(); sz%16 != 0 { + return &ErrILSEQ{Type: ErrFileSize, Name: a.wal.Name(), Arg: sz} + } + + f := bufio.NewReader(a.wal) + items, err := a.readPacket(f) + if err != nil { + return + } + + if len(items) != 3 || items[0] != int64(wpt00Header) || items[1] != int64(walTypeACIDFiler0) { + return &ErrILSEQ{Type: ErrInvalidWAL, Name: a.wal.Name(), More: fmt.Sprintf("invalid packet items %#v", items)} + } + + tr := NewBTree(nil) + + for { + items, err = a.readPacket(f) + if err != nil { + return + } + + if len(items) < 2 { + return &ErrILSEQ{Type: ErrInvalidWAL, Name: a.wal.Name(), More: fmt.Sprintf("too few packet items %#v", items)} + } + + switch items[0] { + case int64(wpt00WriteData): + if len(items) != 3 { + return &ErrILSEQ{Type: ErrInvalidWAL, Name: a.wal.Name(), More: fmt.Sprintf("invalid data packet items %#v", items)} + } + + b, off := items[1].([]byte), items[2].(int64) + var key [8]byte + binary.BigEndian.PutUint64(key[:], uint64(off)) + if err = tr.Set(key[:], b); err != nil { + return + } + case int64(wpt00Checkpoint): + var b1 [1]byte + if n, err := f.Read(b1[:]); n != 0 || err == nil { + return &ErrILSEQ{Type: ErrInvalidWAL, Name: a.wal.Name(), More: fmt.Sprintf("checkpoint n %d, err %v", n, err)} + } + + if len(items) != 2 { + return &ErrILSEQ{Type: ErrInvalidWAL, Name: a.wal.Name(), More: fmt.Sprintf("checkpoint packet invalid items %#v", items)} + } + + sz := items[1].(int64) + enum, err := tr.seekFirst() + if err != nil { + return err + } + + for { + k, v, err := enum.current() + if err != nil { + if fileutil.IsEOF(err) { + break + } + + return err + } + + if _, err = db.WriteAt(v, int64(binary.BigEndian.Uint64(k))); err != nil { + return err + } + + if err = enum.next(); err != nil { + if fileutil.IsEOF(err) { + break + } + + return err + } + } + + if err = db.Truncate(sz); err != nil { + return err + } + + if err = db.Sync(); err != nil { + return err + } + + // Recovery complete + + if err = a.wal.Truncate(0); err != nil { + return err + } + + return a.wal.Sync() + default: + return &ErrILSEQ{Type: ErrInvalidWAL, Name: a.wal.Name(), More: fmt.Sprintf("packet tag %v", items[0])} + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/2pc_docs.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/2pc_docs.go new file mode 100644 index 000000000..02c993b8b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/2pc_docs.go @@ -0,0 +1,44 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + +Anatomy of a WAL file + +WAL file + A sequence of packets + +WAL packet, parts in slice notation + [0:4], 4 bytes: N uint32 // network byte order + [4:4+N], N bytes: payload []byte // gb encoded scalars + +Packets, including the 4 byte 'size' prefix, MUST BE padded to size == 0 (mod +16). The values of the padding bytes MUST BE zero. + +Encoded scalars first item is a packet type number (packet tag). The meaning of +any other item(s) of the payload depends on the packet tag. + +Packet definitions + + {wpt00Header int, typ int, s string} + typ: Must be zero (ACIDFiler0 file). + s: Any comment string, empty string is okay. + + This packet must be present only once - as the first packet of + a WAL file. + + {wpt00WriteData int, b []byte, off int64} + Write data (WriteAt(b, off)). + + {wpt00Checkpoint int, sz int64} + Checkpoint (Truncate(sz)). + + This packet must be present only once - as the last packet of + a WAL file. + +*/ + +package lldb + +//TODO optimize bitfiler/wal/2pc data above final size diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/2pc_test.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/2pc_test.go new file mode 100644 index 000000000..e683b8647 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/2pc_test.go @@ -0,0 +1,285 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Two Phase Commit & Structural ACID + +package lldb + +import ( + "bytes" + "encoding/binary" + "io/ioutil" + "math/rand" + "os" + "testing" + + "github.com/cznic/mathutil" +) + +var _ Filer = &truncFiler{} + +type truncFiler struct { + f Filer + fake *MemFiler + totalWritten int // Including silently dropped + realWritten int + limit int // -1: unlimited, n: silently stop writing after limit bytes +} + +func NewTruncFiler(f Filer, limit int) *truncFiler { + return &truncFiler{f: f, fake: NewMemFiler(), limit: limit} +} + +func (f *truncFiler) BeginUpdate() error { panic("internal error") } +func (f *truncFiler) Close() error { return f.f.Close() } +func (f *truncFiler) EndUpdate() error { panic("internal error") } +func (f *truncFiler) Name() string { return f.f.Name() } +func (f *truncFiler) PunchHole(off, sz int64) error { panic("internal error") } +func (f *truncFiler) ReadAt(b []byte, off int64) (int, error) { return f.fake.ReadAt(b, off) } +func (f *truncFiler) Rollback() error { panic("internal error") } +func (f *truncFiler) Size() (int64, error) { return f.fake.Size() } +func (f *truncFiler) Sync() error { return f.f.Sync() } + +func (f *truncFiler) Truncate(sz int64) error { + f.fake.Truncate(sz) + return f.f.Truncate(sz) +} + +func (f *truncFiler) WriteAt(b []byte, off int64) (n int, err error) { + rq := len(b) + n = f.totalWritten + if lim := f.limit; lim >= 0 && n+rq > lim { + over := n + rq - lim + rq -= over + rq = mathutil.Max(rq, 0) + } + + if n, err = f.fake.WriteAt(b, off); err != nil { + return + } + + f.totalWritten += n + if rq != 0 { + n, err := f.f.WriteAt(b[:rq], off) + if err != nil { + return n, err + } + f.realWritten += n + } + return +} + +// Verify memory BTrees don't have maxRq limits. +func TestACID0MemBTreeCaps(t *testing.T) { + rng := rand.New(rand.NewSource(42)) + tr := NewBTree(nil) + b := make([]byte, 2*maxRq) + for i := range b { + b[i] = byte(rng.Int()) + } + + if err := tr.Set(nil, b); err != nil { + t.Fatal(len(b), err) + } + + g, err := tr.Get(nil, nil) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(g, b) { + t.Fatal("data mismatach") + } +} + +func TestACIDFiler0(t *testing.T) { + const SZ = 1 << 17 + + // Phase 1: Create a DB, fill with it with data. + + wal, err := ioutil.TempFile("", "test-acidfiler0-wal-") + if err != nil { + t.Fatal(err) + } + + if !*oKeep { + defer os.Remove(wal.Name()) + } + + db, err := ioutil.TempFile("", "test-acidfiler0-db-") + if err != nil { + t.Fatal(err) + } + + dbName := db.Name() + if !*oKeep { + defer os.Remove(db.Name()) + } + + realFiler := NewSimpleFileFiler(db) + truncFiler := NewTruncFiler(realFiler, -1) + acidFiler, err := NewACIDFiler(truncFiler, wal) + if err != nil { + t.Error(err) + return + } + + if err = acidFiler.BeginUpdate(); err != nil { + t.Error(err) + return + } + + a, err := NewAllocator(acidFiler, &Options{}) + if err != nil { + t.Error(err) + return + } + + a.Compress = true + + tr, h, err := CreateBTree(a, nil) + if h != 1 || err != nil { + t.Error(h, err) + return + } + + rng := rand.New(rand.NewSource(42)) + var key, val [8]byte + ref := map[int64]int64{} + + for { + sz, err := acidFiler.Size() + if err != nil { + t.Error(err) + return + } + + if sz > SZ { + break + } + + k, v := rng.Int63(), rng.Int63() + ref[k] = v + binary.BigEndian.PutUint64(key[:], uint64(k)) + binary.BigEndian.PutUint64(val[:], uint64(v)) + if err := tr.Set(key[:], val[:]); err != nil { + t.Error(err) + return + } + } + + acidFiler.testHook = true // keep WAL + + if err := acidFiler.EndUpdate(); err != nil { + t.Error(err) + return + } + + if err := acidFiler.Close(); err != nil { + t.Error(err) + return + } + + if err := wal.Sync(); err != nil { + t.Error(err) + return + } + + if _, err = wal.Seek(0, 0); err != nil { + t.Error(err) + return + } + + // Phase 2: Reopen and verify structure and data. + db, err = os.OpenFile(dbName, os.O_RDWR, 0666) + if err != nil { + t.Error(err) + return + } + + filer := NewSimpleFileFiler(db) + a, err = NewAllocator(filer, &Options{}) + if err != nil { + t.Error(err) + return + } + + if err = a.Verify(NewMemFiler(), nil, nil); err != nil { + t.Error(err) + return + } + + tr, err = OpenBTree(a, nil, 1) + for k, v := range ref { + binary.BigEndian.PutUint64(key[:], uint64(k)) + binary.BigEndian.PutUint64(val[:], uint64(v)) + var b []byte + b, err = tr.Get(b, key[:]) + if err != nil || b == nil || !bytes.Equal(b, val[:]) { + t.Error(err, b, val[:]) + return + } + } + + okImage, err := ioutil.ReadFile(dbName) + if err != nil { + t.Error(err) + return + } + + // Phase 3: Simulate a crash + sz, err := filer.Size() + if err != nil { + t.Error(err) + return + } + + sz /= 2 + if err := db.Truncate(sz); err != nil { + t.Error(err) + return + } + + z := make([]byte, sz/3) + n, err := db.WriteAt(z, sz/3) + if n != len(z) { + t.Error(n, err) + return + } + + if err := db.Sync(); err != nil { + t.Error(err) + return + } + + // Phase 4: Open the corrupted DB + filer = NewSimpleFileFiler(db) + acidFiler, err = NewACIDFiler(filer, wal) + if err != nil { + t.Error(err) + return + } + + if err = acidFiler.Sync(); err != nil { + t.Error(err) + return + } + + if err = acidFiler.Close(); err != nil { + t.Error(err) + return + } + + // Phase 5: Verify DB was recovered. + newImage, err := ioutil.ReadFile(dbName) + if err != nil { + t.Error(err) + return + } + + if !bytes.Equal(okImage, newImage) { + t.Error(err) + return + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/AUTHORS b/Godeps/_workspace/src/github.com/cznic/exp/lldb/AUTHORS new file mode 100644 index 000000000..0078f5f5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/AUTHORS @@ -0,0 +1,11 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/exp/lldb/CONTRIBUTORS new file mode 100644 index 000000000..5e86f0607 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/CONTRIBUTORS @@ -0,0 +1,9 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/LICENSE b/Godeps/_workspace/src/github.com/cznic/exp/lldb/LICENSE new file mode 100644 index 000000000..27e4447a4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The lldb Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/Makefile b/Godeps/_workspace/src/github.com/cznic/exp/lldb/Makefile new file mode 100644 index 000000000..cd8248a8e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/Makefile @@ -0,0 +1,45 @@ +# Copyright 2014 The lldb Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all editor clean cover nuke + +testbin=lldb.test +grep=--include=*.go + +all: editor + go build + go vet + golint . + go install + make todo + +clean: + go clean + rm -f *~ cov cov.html bad-dump good-dump lldb.test old.txt new.txt \ + test-acidfiler0-* _test.db _wal + +cover: + t=$(shell tempfile) ; go test -coverprofile $$t && go tool cover -html $$t && unlink $$t + +editor: + go fmt + go test -i + go test -timeout 1h + +mem: + go test -c + ./$(testbin) -test.bench . -test.memprofile mem.out -test.memprofilerate 1 -test.timeout 24h + go tool pprof --lines --web --alloc_space $(testbin) mem.out + +nuke: clean + go clean -i + +todo: + @grep -nr $(grep) BUG * || true + @grep -nr $(grep) LATER * || true + @grep -nr $(grep) MAYBE * || true + @grep -nr $(grep) TODO * || true + @grep -nr $(grep) FIXME * || true + @grep -nr $(grep) ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* * || true + @grep -nr $(grep) println * || true diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/README.md b/Godeps/_workspace/src/github.com/cznic/exp/lldb/README.md new file mode 100644 index 000000000..470bf541d --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/README.md @@ -0,0 +1,8 @@ +lldb +==== + +Package lldb (WIP) implements a low level database engine. + +Installation: $ go get github.com/cznic/exp/lldb + +Documentation: [godoc.org/github.com/cznic/exp/lldb](http://godoc.org/github.com/cznic/exp/lldb) diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/all_test.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/all_test.go new file mode 100644 index 000000000..451302de3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/all_test.go @@ -0,0 +1,43 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lldb + +import ( + "encoding/hex" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "time" +) + +const ( + testDbName = "_test.db" + walName = "_wal" +) + +func now() time.Time { return time.Now() } + +func hdump(b []byte) string { + return hex.Dump(b) +} + +func die() { + os.Exit(1) +} + +func stack() string { + buf := make([]byte, 1<<16) + return string(buf[:runtime.Stack(buf, false)]) +} + +func temp() (dir, name string) { + dir, err := ioutil.TempDir("", "test-lldb-") + if err != nil { + panic(err) + } + + return dir, filepath.Join(dir, "test.tmp") +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/btree.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/btree.go new file mode 100644 index 000000000..57b0f39c6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/btree.go @@ -0,0 +1,2297 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lldb + +import ( + "bytes" + "errors" + "fmt" + "io" + "sort" + "strings" + + "github.com/cznic/bufs" + "github.com/cznic/fileutil" + "github.com/cznic/sortutil" +) + +const ( + kData = 256 // [1, 512] + kIndex = 256 // [2, 2048] + kKV = 19 // Size of the key/value field in btreeDataPage + kSz = kKV - 1 - 7 // Content prefix size + kH = kKV - 7 // Content field offset for handle + tagBTreeDataPage = 1 + tagBTreeIndexPage = 0 +) + +// BTree is a B+tree[1][2], i.e. a variant which speeds up +// enumeration/iteration of the BTree. According to its origin it can be +// volatile (backed only by memory) or non-volatile (backed by a non-volatile +// Allocator). +// +// The specific implementation of BTrees in this package are B+trees with +// delayed split/concatenation (discussed in e.g. [3]). +// +// Note: No BTree methods returns io.EOF for physical Filer reads/writes. The +// io.EOF is returned only by bTreeEnumerator methods to indicate "no more K-V +// pair". +// +// [1]: http://en.wikipedia.org/wiki/B+tree +// [2]: http://zgking.com:8080/home/donghui/publications/books/dshandbook_BTree.pdf +// [3]: http://people.cs.aau.dk/~simas/aalg06/UbiquitBtree.pdf +type BTree struct { + store btreeStore + root btree + collate func(a, b []byte) int + serial uint64 +} + +// NewBTree returns a new, memory-only BTree. +func NewBTree(collate func(a, b []byte) int) *BTree { + store := newMemBTreeStore() + root, err := newBTree(store) + if err != nil { // should not happen + panic(err.Error()) + } + + return &BTree{store, root, collate, 0} +} + +// IsMem reports if t is a memory only BTree. +func (t *BTree) IsMem() (r bool) { + _, r = t.store.(*memBTreeStore) + return +} + +// Clear empties the tree. +func (t *BTree) Clear() (err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + t.serial++ + return t.root.clear(t.store) +} + +// Delete deletes key and its associated value from the tree. +func (t *BTree) Delete(key []byte) (err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + t.serial++ + _, err = t.root.extract(t.store, nil, t.collate, key) + return +} + +// DeleteAny deletes one key and its associated value from the tree. If the +// tree is empty on return then empty is true. +func (t *BTree) DeleteAny() (empty bool, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + t.serial++ + return t.root.deleteAny(t.store) +} + +func elem(v interface{}) string { + switch x := v.(type) { + default: + panic("internal error") + case nil: + return "nil" + case bool: + if x { + return "true" + } + + return "false" + case int64: + return fmt.Sprint(x) + case uint64: + return fmt.Sprint(x) + case float64: + s := fmt.Sprintf("%g", x) + if !strings.Contains(s, ".") { + s += "." + } + return s + case complex128: + s := fmt.Sprint(x) + return s[1 : len(s)-1] + case []byte: + return fmt.Sprintf("[]byte{% 02x}", x) + case string: + return fmt.Sprintf("%q", x) + } +} + +// Dump outputs a human readable dump of t to w. It is usable iff t keys and +// values are encoded scalars (see EncodeScalars). Intended use is only for +// examples or debugging. Some type information is lost in the rendering, for +// example a float value '17.' and an integer value '17' may both output as +// '17'. +func (t *BTree) Dump(w io.Writer) (err error) { + enum, err := t.seekFirst() + if err != nil { + return + } + + for { + bkey, bval, err := enum.current() + if err != nil { + return err + } + + key, err := DecodeScalars(bkey) + if err != nil { + return err + } + + val, err := DecodeScalars(bval) + if err != nil { + return err + } + + kk := []string{} + if key == nil { + kk = []string{"null"} + } + for _, v := range key { + kk = append(kk, elem(v)) + } + vv := []string{} + if val == nil { + vv = []string{"null"} + } + for _, v := range val { + vv = append(vv, elem(v)) + } + skey := strings.Join(kk, ", ") + sval := strings.Join(vv, ", ") + if len(vv) > 1 { + sval = fmt.Sprintf("[]interface{%s}", sval) + } + if _, err = fmt.Fprintf(w, "%s → %s\n", skey, sval); err != nil { + return err + } + + err = enum.next() + if err != nil { + if fileutil.IsEOF(err) { + err = nil + break + } + + return err + } + } + return +} + +// Extract is a combination of Get and Delete. If the key exists in the tree, +// it is returned (like Get) and also deleted from a tree in a more efficient +// way which doesn't walk it twice. The returned slice may be a sub-slice of +// buf if buf was large enough to hold the entire content. Otherwise, a newly +// allocated slice will be returned. It is valid to pass a nil buf. +func (t *BTree) Extract(buf, key []byte) (value []byte, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + t.serial++ + return t.root.extract(t.store, buf, t.collate, key) +} + +// First returns the first KV pair of the tree, if it exists. Otherwise key == nil +// and value == nil. +func (t *BTree) First() (key, value []byte, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + var p btreeDataPage + if _, p, err = t.root.first(t.store); err != nil || p == nil { + return + } + + if key, err = p.key(t.store, 0); err != nil { + return + } + + value, err = p.value(t.store, 0) + return +} + +// Get returns the value associated with key, or nil if no such value exists. +// The returned slice may be a sub-slice of buf if buf was large enough to hold +// the entire content. Otherwise, a newly allocated slice will be returned. +// It is valid to pass a nil buf. +// +// Get is safe for concurrent access by multiple goroutines iff no other +// goroutine mutates the tree. +func (t *BTree) Get(buf, key []byte) (value []byte, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + buffer := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(buffer) + if buffer, err = t.root.get(t.store, buffer, t.collate, key); buffer == nil || err != nil { + return + } + + value = need(len(buffer), buf) + copy(value, buffer) + return +} + +// Handle reports t's handle. +func (t *BTree) Handle() int64 { + return int64(t.root) +} + +// Last returns the last KV pair of the tree, if it exists. Otherwise key == nil +// and value == nil. +func (t *BTree) Last() (key, value []byte, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + var p btreeDataPage + if _, p, err = t.root.last(t.store); err != nil || p == nil { + return + } + + index := p.len() - 1 + if key, err = p.key(t.store, index); err != nil { + return + } + + value, err = p.value(t.store, index) + return +} + +// Put combines Get and Set in a more efficient way where the tree is walked +// only once. The upd(ater) receives the current (key, old-value), if that +// exists or (key, nil) otherwise. It can then return a (new-value, true, nil) +// to create or overwrite the existing value in the KV pair, or (whatever, +// false, nil) if it decides not to create or not to update the value of the KV +// pair. +// +// tree.Set(k, v) +// +// conceptually equals +// +// tree.Put(k, func(k, v []byte){ return v, true }([]byte, bool)) +// +// modulo the differing return values. +// +// The returned slice may be a sub-slice of buf if buf was large enough to hold +// the entire content. Otherwise, a newly allocated slice will be returned. +// It is valid to pass a nil buf. +func (t *BTree) Put(buf, key []byte, upd func(key, old []byte) (new []byte, write bool, err error)) (old []byte, written bool, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + t.serial++ + return t.root.put2(buf, t.store, t.collate, key, upd) +} + +// Seek returns an Enumerator with "position" or an error of any. Normally the +// position is on a KV pair such that key >= KV.key. Then hit is key == KV.key. +// The position is possibly "after" the last KV pair, but that is not an error. +// +// Seek is safe for concurrent access by multiple goroutines iff no other +// goroutine mutates the tree. +func (t *BTree) Seek(key []byte) (enum *BTreeEnumerator, hit bool, err error) { + enum0, hit, err := t.seek(key) + if err != nil { + return + } + + enum = &BTreeEnumerator{ + enum: enum0, + firstHit: hit, + key: append([]byte(nil), key...), + } + return +} + +func (t *BTree) seek(key []byte) (enum *bTreeEnumerator, hit bool, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + r := &bTreeEnumerator{t: t, collate: t.collate, serial: t.serial} + if r.p, r.index, hit, err = t.root.seek(t.store, r.collate, key); err != nil { + return + } + + enum = r + return +} + +// IndexSeek returns an Enumerator with "position" or an error of any. Normally +// the position is on a KV pair such that key >= KV.key. Then hit is key == +// KV.key. The position is possibly "after" the last KV pair, but that is not +// an error. The collate function originally passed to CreateBTree is used for +// enumerating the tree but a custom collate function c is used for IndexSeek. +// +// IndexSeek is safe for concurrent access by multiple goroutines iff no other +// goroutine mutates the tree. +func (t *BTree) IndexSeek(key []byte, c func(a, b []byte) int) (enum *BTreeEnumerator, hit bool, err error) { //TODO +test + enum0, hit, err := t.indexSeek(key, c) + if err != nil { + return + } + + enum = &BTreeEnumerator{ + enum: enum0, + firstHit: hit, + key: append([]byte(nil), key...), + } + return +} + +func (t *BTree) indexSeek(key []byte, c func(a, b []byte) int) (enum *bTreeEnumerator, hit bool, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + r := &bTreeEnumerator{t: t, collate: t.collate, serial: t.serial} + if r.p, r.index, hit, err = t.root.seek(t.store, c, key); err != nil { + return + } + + enum = r + return +} + +// seekFirst returns an enumerator positioned on the first KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returend. +// +// SeekFirst is safe for concurrent access by multiple goroutines iff no other +// goroutine mutates the tree. +func (t *BTree) SeekFirst() (enum *BTreeEnumerator, err error) { + enum0, err := t.seekFirst() + if err != nil { + return + } + + var key []byte + if key, _, err = enum0.current(); err != nil { + return + } + + enum = &BTreeEnumerator{ + enum: enum0, + firstHit: true, + key: append([]byte(nil), key...), + } + return +} + +func (t *BTree) seekFirst() (enum *bTreeEnumerator, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + var p btreeDataPage + if _, p, err = t.root.first(t.store); err == nil && p == nil { + err = io.EOF + } + if err != nil { + return + } + + return &bTreeEnumerator{t: t, collate: t.collate, p: p, index: 0, serial: t.serial}, nil +} + +// seekLast returns an enumerator positioned on the last KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returend. +// +// SeekLast is safe for concurrent access by multiple goroutines iff no other +// goroutine mutates the tree. +func (t *BTree) SeekLast() (enum *BTreeEnumerator, err error) { + enum0, err := t.seekLast() + if err != nil { + return + } + + var key []byte + if key, _, err = enum0.current(); err != nil { + return + } + + enum = &BTreeEnumerator{ + enum: enum0, + firstHit: true, + key: append([]byte(nil), key...), + } + return +} + +func (t *BTree) seekLast() (enum *bTreeEnumerator, err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + var p btreeDataPage + if _, p, err = t.root.last(t.store); err == nil && p == nil { + err = io.EOF + } + if err != nil { + return + } + + return &bTreeEnumerator{t: t, collate: t.collate, p: p, index: p.len() - 1, serial: t.serial}, nil +} + +// Set sets the value associated with key. Any previous value, if existed, is +// overwritten by the new one. +func (t *BTree) Set(key, value []byte) (err error) { + if t == nil { + err = errors.New("BTree method invoked on nil receiver") + return + } + + t.serial++ + dst := bufs.GCache.Get(maxBuf) + _, err = t.root.put(dst, t.store, t.collate, key, value, true) + bufs.GCache.Put(dst) + return +} + +// bTreeEnumerator is a closure of a BTree and a position. It is returned from +// BTree.seek. +// +// NOTE: bTreeEnumerator cannot be used after its BTree was mutated after the +// bTreeEnumerator was acquired from any of the seek, seekFirst, seekLast +// methods. +type bTreeEnumerator struct { + t *BTree + collate func(a, b []byte) int + p btreeDataPage + index int + serial uint64 +} + +// Current returns the KV pair the enumerator is currently positioned on. If +// the position is before the first KV pair in the tree or after the last KV +// pair in the tree then err == io.EOF is returned. +// +// If the enumerator has been invalidated by updating the tree, ErrINVAL is +// returned. +func (e *bTreeEnumerator) current() (key, value []byte, err error) { + if e == nil { + err = errors.New("bTreeEnumerator method invoked on nil receiver") + return + } + + if e.serial != e.t.serial { + err = &ErrINVAL{Src: "bTreeEnumerator invalidated by updating the tree"} + return + } + + if e.p == nil || e.index == e.p.len() { + return nil, nil, io.EOF + } + + if key, err = e.p.key(e.t.store, e.index); err != nil { + return + } + + value, err = e.p.value(e.t.store, e.index) + return +} + +// Next attempts to position the enumerator onto the next KV pair wrt the +// current position. If there is no "next" KV pair, io.EOF is returned. +// +// If the enumerator has been invalidated by updating the tree, ErrINVAL is +// returned. +func (e *bTreeEnumerator) next() (err error) { + if e == nil { + err = errors.New("bTreeEnumerator method invoked on nil receiver") + return + } + + if e.serial != e.t.serial { + err = &ErrINVAL{Src: "bTreeEnumerator invalidated by updating the tree"} + return + } + + if e.p == nil { + return io.EOF + } + + switch { + case e.index < e.p.len()-1: + e.index++ + default: + ph := e.p.next() + if ph == 0 { + err = io.EOF + break + } + + if e.p, err = e.t.store.Get(e.p, ph); err != nil { + e.p = nil + return + } + e.index = 0 + } + return +} + +// Prev attempts to position the enumerator onto the previous KV pair wrt the +// current position. If there is no "previous" KV pair, io.EOF is returned. +// +// If the enumerator has been invalidated by updating the tree, ErrINVAL is +// returned. +func (e *bTreeEnumerator) prev() (err error) { + if e == nil { + err = errors.New("bTreeEnumerator method invoked on nil receiver") + return + } + + if e.serial != e.t.serial { + err = &ErrINVAL{Src: "bTreeEnumerator invalidated by updating the tree"} + return + } + + if e.p == nil { + return io.EOF + } + + switch { + case e.index > 0: + e.index-- + default: + ph := e.p.prev() + if ph == 0 { + err = io.EOF + break + } + + if e.p, err = e.t.store.Get(e.p, ph); err != nil { + e.p = nil + return + } + e.index = e.p.len() - 1 + } + return +} + +// BTreeEnumerator captures the state of enumerating a tree. It is returned +// from the Seek* methods. The enumerator is aware of any mutations made to +// the tree in the process of enumerating it and automatically resumes the +// enumeration. +type BTreeEnumerator struct { + enum *bTreeEnumerator + err error + key []byte + firstHit bool +} + +// Next returns the currently enumerated KV pair, if it exists and moves to the +// next KV in the key collation order. If there is no KV pair to return, err == +// io.EOF is returned. +// +// Next is safe for concurrent access by multiple goroutines iff no other +// goroutine mutates the tree. +func (e *BTreeEnumerator) Next() (key, value []byte, err error) { + if err = e.err; err != nil { + return + } + + canRetry := true +retry: + if key, value, err = e.enum.current(); err != nil { + if _, ok := err.(*ErrINVAL); !ok || !canRetry { + e.err = err + return + } + + canRetry = false + var hit bool + if e.enum, hit, err = e.enum.t.seek(e.key); err != nil { + e.err = err + return + } + + if !e.firstHit && hit { + err = e.enum.next() + if err != nil { + e.err = err + return + } + } + + goto retry + } + + e.firstHit = false + e.key = append([]byte(nil), key...) + e.err = e.enum.next() + return +} + +// Prev returns the currently enumerated KV pair, if it exists and moves to the +// previous KV in the key collation order. If there is no KV pair to return, +// err == io.EOF is returned. +// +// Prev is safe for concurrent access by multiple goroutines iff no other +// goroutine mutates the tree. +func (e *BTreeEnumerator) Prev() (key, value []byte, err error) { + if err = e.err; err != nil { + return + } + + canRetry := true +retry: + if key, value, err = e.enum.current(); err != nil { + if _, ok := err.(*ErrINVAL); !ok || !canRetry { + e.err = err + return + } + + canRetry = false + var hit bool + if e.enum, hit, err = e.enum.t.seek(e.key); err != nil { + e.err = err + return + } + + if !e.firstHit && hit { + err = e.enum.prev() + if err != nil { + e.err = err + return + } + } + + goto retry + } + + e.firstHit = false + e.key = append([]byte(nil), key...) + e.err = e.enum.prev() + return +} + +// CreateBTree creates a new BTree in store. It returns the tree, its (freshly +// assigned) handle (for OpenBTree or RemoveBTree) or an error, if any. +func CreateBTree(store *Allocator, collate func(a, b []byte) int) (bt *BTree, handle int64, err error) { + r := &BTree{store: store, collate: collate} + if r.root, err = newBTree(store); err != nil { + return + } + + return r, int64(r.root), nil +} + +// OpenBTree opens a store's BTree using handle. It returns the tree or an +// error, if any. The same tree may be opened more than once, but operations on +// the separate instances should not ever overlap or void the other instances. +// However, the intended API usage is to open the same tree handle only once +// (handled by some upper layer "dispatcher"). +func OpenBTree(store *Allocator, collate func(a, b []byte) int, handle int64) (bt *BTree, err error) { + r := &BTree{store: store, root: btree(handle), collate: collate} + b := bufs.GCache.Get(7) + defer bufs.GCache.Put(b) + if b, err = store.Get(b, handle); err != nil { + return + } + + if len(b) != 7 { + return nil, &ErrILSEQ{Off: h2off(handle), More: "btree.go:671"} + } + + return r, nil +} + +// RemoveBTree removes tree, represented by handle from store. Empty trees are +// cheap, each uses only few bytes of the store. If there's a chance that a +// tree will eventually get reused (non empty again), it's recommended to +// not/never remove it. One advantage of such approach is a stable handle of +// such tree. +func RemoveBTree(store *Allocator, handle int64) (err error) { + tree, err := OpenBTree(store, nil, handle) + if err != nil { + return + } + + if err = tree.Clear(); err != nil { + return + } + + return store.Free(handle) +} + +type btreeStore interface { + Alloc(b []byte) (handle int64, err error) + Free(handle int64) (err error) + Get(dst []byte, handle int64) (b []byte, err error) + Realloc(handle int64, b []byte) (err error) +} + +// Read only zero bytes +var zeros [2 * kKV]byte + +func init() { + if kData < 1 || kData > 512 { + panic(fmt.Errorf("kData %d: out of limits", kData)) + } + + if kIndex < 2 || kIndex > 2048 { + panic(fmt.Errorf("kIndex %d: out of limits", kIndex)) + } + + if kKV < 8 || kKV > 23 { + panic(fmt.Errorf("kKV %d: out of limits", kKV)) + } + + if n := len(zeros); n < 15 { + panic(fmt.Errorf("not enough zeros: %d", n)) + } +} + +type memBTreeStore struct { + h int64 + m map[int64][]byte +} + +func newMemBTreeStore() *memBTreeStore { + return &memBTreeStore{h: 0, m: map[int64][]byte{}} +} + +func (s *memBTreeStore) String() string { + var a sortutil.Int64Slice + for k := range s.m { + a = append(a, k) + } + sort.Sort(a) + var sa []string + for _, k := range a { + sa = append(sa, fmt.Sprintf("%#x:|% x|", k, s.m[k])) + } + return strings.Join(sa, "\n") +} + +func (s *memBTreeStore) Alloc(b []byte) (handle int64, err error) { + s.h++ + handle = s.h + s.m[handle] = bpack(b) + return +} + +func (s *memBTreeStore) Free(handle int64) (err error) { + if _, ok := s.m[handle]; !ok { + return &ErrILSEQ{Type: ErrOther, Off: h2off(handle), More: "btree.go:754"} + } + + delete(s.m, handle) + return +} + +func (s *memBTreeStore) Get(dst []byte, handle int64) (b []byte, err error) { + r, ok := s.m[handle] + if !ok { + return nil, &ErrILSEQ{Type: ErrOther, Off: h2off(handle), More: "btree.go:764"} + } + + b = need(len(r), dst) + copy(b, r) + return +} + +func (s *memBTreeStore) Realloc(handle int64, b []byte) (err error) { + if _, ok := s.m[handle]; !ok { + return &ErrILSEQ{Type: ErrOther, Off: h2off(handle), More: "btree.go:774"} + } + + s.m[handle] = bpack(b) + return +} + +/* + +0...0 (1 bytes): +Flag + + 0 + +---+ + | 0 | + +---+ + +0 indicates an index page + +1...count*14-1 +"array" of items, 14 bytes each. Count of items in kIndex-1..2*kIndex+2 + + Count = (len(raw) - 8) / 14 + + 0..6 7..13 + +-------+----------+ + | Child | DataPage | + +-------+----------+ + + Child == handle of a child index page + DataPage == handle of a data page + +Offsets into the raw []byte: +Child[X] == 1+14*X +DataPage[X] == 8+14*X + +*/ +type btreeIndexPage []byte + +func newBTreeIndexPage(leftmostChild int64) (p btreeIndexPage) { + p = bufs.GCache.Get(1 + (kIndex+1)*2*7)[:8] + p[0] = tagBTreeIndexPage + h2b(p[1:], leftmostChild) + return +} + +func (p btreeIndexPage) len() int { + return (len(p) - 8) / 14 +} + +func (p btreeIndexPage) child(index int) int64 { + return b2h(p[1+14*index:]) +} + +func (p btreeIndexPage) setChild(index int, dp int64) { + h2b(p[1+14*index:], dp) +} + +func (p btreeIndexPage) dataPage(index int) int64 { + return b2h(p[8+14*index:]) +} + +func (p btreeIndexPage) setDataPage(index int, dp int64) { + h2b(p[8+14*index:], dp) +} + +func (q btreeIndexPage) insert(index int) btreeIndexPage { + switch len0 := q.len(); { + case index < len0: + has := len(q) + need := has + 14 + switch { + case cap(q) >= need: + q = q[:need] + default: + q = append(q, zeros[:14]...) + } + copy(q[8+14*(index+1):8+14*(index+1)+2*(len0-index)*7], q[8+14*index:]) + case index == len0: + has := len(q) + need := has + 14 + switch { + case cap(q) >= need: + q = q[:need] + default: + q = append(q, zeros[:14]...) + } + } + return q +} + +func (p btreeIndexPage) insert3(index int, dataPage, child int64) btreeIndexPage { + p = p.insert(index) + p.setDataPage(index, dataPage) + p.setChild(index+1, child) + return p +} + +func (p btreeIndexPage) cmp(a btreeStore, c func(a, b []byte) int, keyA []byte, keyBIndex int) (int, error) { + b := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(b) + dp, err := a.Get(b, p.dataPage(keyBIndex)) + if err != nil { + return 0, err + } + + return btreeDataPage(dp).cmp(a, c, keyA, 0) +} + +func (q btreeIndexPage) setLen(n int) btreeIndexPage { + q = q[:cap(q)] + need := 8 + 14*n + if need < len(q) { + return q[:need] + } + return append(q, make([]byte, need-len(q))...) +} + +func (p btreeIndexPage) split(a btreeStore, root btree, ph *int64, parent int64, parentIndex int, index *int) (btreeIndexPage, error) { + right := newBTreeIndexPage(0) + canRecycle := true + defer func() { + if canRecycle { + bufs.GCache.Put(right) + } + }() + right = right.setLen(kIndex) + copy(right[1:1+(2*kIndex+1)*7], p[1+14*(kIndex+1):]) + p = p.setLen(kIndex) + if err := a.Realloc(*ph, p); err != nil { + return nil, err + } + + rh, err := a.Alloc(right) + if err != nil { + return nil, err + } + + if parentIndex >= 0 { + var pp btreeIndexPage = bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(pp) + if pp, err = a.Get(pp, parent); err != nil { + return nil, err + } + pp = pp.insert3(parentIndex, p.dataPage(kIndex), rh) + if err = a.Realloc(parent, pp); err != nil { + return nil, err + } + + } else { + nr := newBTreeIndexPage(*ph) + defer bufs.GCache.Put(nr) + nr = nr.insert3(0, p.dataPage(kIndex), rh) + nrh, err := a.Alloc(nr) + if err != nil { + return nil, err + } + + if err = a.Realloc(int64(root), h2b(make([]byte, 7), nrh)); err != nil { + return nil, err + } + } + if *index > kIndex { + p = right + canRecycle = false + *ph = rh + *index -= kIndex + 1 + } + return p, nil +} + +// p is dirty on return +func (p btreeIndexPage) extract(index int) btreeIndexPage { + n := p.len() - 1 + if index < n { + sz := (n-index)*14 + 7 + copy(p[1+14*index:1+14*index+sz], p[1+14*(index+1):]) + } + return p.setLen(n) +} + +// must persist all changes made +func (p btreeIndexPage) underflow(a btreeStore, root, iroot, parent int64, ph *int64, parentIndex int, index *int) (btreeIndexPage, error) { + lh, rh, err := checkSiblings(a, parent, parentIndex) + if err != nil { + return nil, err + } + + var left btreeIndexPage = bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(left) + + if lh != 0 { + if left, err = a.Get(left, lh); err != nil { + return nil, err + } + + if lc := btreeIndexPage(left).len(); lc > kIndex { + var pp = bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(pp) + if pp, err = a.Get(pp, parent); err != nil { + return nil, err + } + + pc := p.len() + p = p.setLen(pc + 1) + di, si, sz := 1+1*14, 1+0*14, (2*pc+1)*7 + copy(p[di:di+sz], p[si:]) + p.setChild(0, btreeIndexPage(left).child(lc)) + p.setDataPage(0, btreeIndexPage(pp).dataPage(parentIndex-1)) + *index++ + btreeIndexPage(pp).setDataPage(parentIndex-1, btreeIndexPage(left).dataPage(lc-1)) + left = left.setLen(lc - 1) + if err = a.Realloc(parent, pp); err != nil { + return nil, err + } + + if err = a.Realloc(*ph, p); err != nil { + return nil, err + } + + return p, a.Realloc(lh, left) + } + } + + if rh != 0 { + right := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(right) + if right, err = a.Get(right, rh); err != nil { + return nil, err + } + + if rc := btreeIndexPage(right).len(); rc > kIndex { + pp := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(pp) + if pp, err = a.Get(pp, parent); err != nil { + return nil, err + } + + pc := p.len() + p = p.setLen(pc + 1) + p.setDataPage(pc, btreeIndexPage(pp).dataPage(parentIndex)) + pc++ + p.setChild(pc, btreeIndexPage(right).child(0)) + btreeIndexPage(pp).setDataPage(parentIndex, btreeIndexPage(right).dataPage(0)) + di, si, sz := 1+0*14, 1+1*14, (2*rc+1)*7 + copy(right[di:di+sz], right[si:]) + right = btreeIndexPage(right).setLen(rc - 1) + if err = a.Realloc(parent, pp); err != nil { + return nil, err + } + + if err = a.Realloc(*ph, p); err != nil { + return nil, err + } + + return p, a.Realloc(rh, right) + } + } + + if lh != 0 { + *index += left.len() + 1 + if left, err = left.concat(a, root, iroot, parent, lh, *ph, parentIndex-1); err != nil { + return p, err + } + + p, *ph = left, lh + return p, nil + } + + return p.concat(a, root, iroot, parent, *ph, rh, parentIndex) +} + +// must persist all changes made +func (p btreeIndexPage) concat(a btreeStore, root, iroot, parent, ph, rh int64, parentIndex int) (btreeIndexPage, error) { + pp := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(pp) + pp, err := a.Get(pp, parent) + if err != nil { + return nil, err + } + + right := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(right) + if right, err = a.Get(right, rh); err != nil { + return nil, err + } + + pc := p.len() + rc := btreeIndexPage(right).len() + p = p.setLen(pc + rc + 1) + p.setDataPage(pc, btreeIndexPage(pp).dataPage(parentIndex)) + di, si, sz := 1+14*(pc+1), 1+0*14, (2*rc+1)*7 + copy(p[di:di+sz], right[si:]) + if err := a.Realloc(ph, p); err != nil { + return nil, err + } + + if err := a.Free(rh); err != nil { + return nil, err + } + + if pc := btreeIndexPage(pp).len(); pc > 1 { + if parentIndex < pc-1 { + di, si, sz := 8+parentIndex*14, 8+(parentIndex+1)*14, 2*(pc-1-parentIndex)*7 + copy(pp[di:si+sz], pp[si:]) + } + pp = btreeIndexPage(pp).setLen(pc - 1) + return p, a.Realloc(parent, pp) + } + + if err := a.Free(iroot); err != nil { + return nil, err + } + + b7 := bufs.GCache.Get(7) + defer bufs.GCache.Put(b7) + return p, a.Realloc(root, h2b(b7[:7], ph)) +} + +/* + +0...0 (1 bytes): +Flag + + 0 + +---+ + | 1 | + +---+ + +1 indicates a data page + +1...14 (14 bytes) + + 1..7 8..14 + +------+------+ + | Prev | Next | + +------+------+ + + Prev, Next == Handles of the data pages doubly linked list + + Count = (len(raw) - 15) / (2*kKV) + +15...count*2*kKV-1 +"array" of items, 2*kKV bytes each. Count of items in kData-1..2*kData + +Item + 0..kKV-1 kKV..2*kKV-1 + +----------+--------------+ + | Key | Value | + +----------+--------------+ + +Key/Value encoding + +Length 0...kKV-1 + + 0 1...N N+1...kKV-1 + +---+---------+-------------+ + | N | Data | Padding | + +---+---------+-------------+ + + N == content length + Data == Key or Value content + Padding == MUST be zero bytes + +Length >= kKV + + 0 1...kkV-8 kKV-7...kkV-1 + +------+-----------+--------------+ + | 0xFF | Data | H | + +------+-----------+--------------+ + + Data == Key or Value content, first kKV-7 bytes + H == Handle to THE REST of the content, w/o the first bytes in Data. + +Offsets into the raw []byte: +Key[X] == 15+2*kKV*X +Value[X] == 15+kKV+2*kKV*X +*/ +type btreeDataPage []byte + +func newBTreeDataPage() (p btreeDataPage) { + p = bufs.GCache.Cget(1 + 2*7 + (kData+1)*2*kKV)[:1+2*7] + p[0] = tagBTreeDataPage + return +} + +func newBTreeDataPageAlloc(a btreeStore) (p btreeDataPage, h int64, err error) { + p = newBTreeDataPage() + h, err = a.Alloc(p) + return +} + +func (p btreeDataPage) len() int { + return (len(p) - 15) / (2 * kKV) +} + +func (q btreeDataPage) setLen(n int) btreeDataPage { + q = q[:cap(q)] + need := 15 + 2*kKV*n + if need < len(q) { + return q[:need] + } + return append(q, make([]byte, need-len(q))...) +} + +func (p btreeDataPage) prev() int64 { + return b2h(p[1:]) +} + +func (p btreeDataPage) next() int64 { + return b2h(p[8:]) +} + +func (p btreeDataPage) setPrev(h int64) { + h2b(p[1:], h) +} + +func (p btreeDataPage) setNext(h int64) { + h2b(p[8:], h) +} + +func (q btreeDataPage) insert(index int) btreeDataPage { + switch len0 := q.len(); { + case index < len0: + has := len(q) + need := has + 2*kKV + switch { + case cap(q) >= need: + q = q[:need] + default: + q = append(q, zeros[:2*kKV]...) + } + q.copy(q, index+1, index, len0-index) + return q + case index == len0: + has := len(q) + need := has + 2*kKV + switch { + case cap(q) >= need: + return q[:need] + default: + return append(q, zeros[:2*kKV]...) + } + } + panic("internal error") +} + +func (p btreeDataPage) contentField(off int) (b []byte, h int64) { + p = p[off:] + switch n := int(p[0]); { + case n >= kKV: // content has a handle + b = append([]byte(nil), p[1:1+kSz]...) + h = b2h(p[kH:]) + default: // content is embedded + b, h = append([]byte(nil), p[1:1+n]...), 0 + } + return +} + +func (p btreeDataPage) content(a btreeStore, off int) (b []byte, err error) { + b, h := p.contentField(off) + if h == 0 { + return + } + + // content has a handle + b2, err := a.Get(nil, h) //TODO buffers: Later, not a public API + if err != nil { + return nil, err + } + + return append(b, b2...), nil +} + +func (p btreeDataPage) setContent(a btreeStore, off int, b []byte) (err error) { + p = p[off:] + switch { + case p[0] >= kKV: // existing content has a handle + switch n := len(b); { + case n < kKV: + p[0] = byte(n) + if err = a.Free(b2h(p[kH:])); err != nil { + return + } + copy(p[1:], b) + default: + // reuse handle + copy(p[1:1+kSz], b) + return a.Realloc(b2h(p[kH:]), b[kSz:]) + } + default: // existing content is embedded + switch n := len(b); { + case n < kKV: + p[0] = byte(n) + copy(p[1:], b) + default: + p[0] = 0xff + copy(p[1:1+kSz], b) + h, err := a.Alloc(b[kSz:]) + if err != nil { + return err + } + + h2b(p[kH:], h) + } + } + return +} + +func (p btreeDataPage) keyField(index int) (b []byte, h int64) { + return p.contentField(15 + 2*kKV*index) +} + +func (p btreeDataPage) key(a btreeStore, index int) (b []byte, err error) { + return p.content(a, 15+2*kKV*index) +} + +func (p btreeDataPage) valueField(index int) (b []byte, h int64) { + return p.contentField(15 + kKV + 2*kKV*index) +} + +func (p btreeDataPage) value(a btreeStore, index int) (b []byte, err error) { + return p.content(a, 15+kKV+2*kKV*index) +} + +func (p btreeDataPage) valueCopy(a btreeStore, index int) (b []byte, err error) { + if b, err = p.content(a, 15+kKV+2*kKV*index); err != nil { + return + } + + return append([]byte(nil), b...), nil +} + +func (p btreeDataPage) setKey(a btreeStore, index int, key []byte) (err error) { + return p.setContent(a, 15+2*kKV*index, key) +} + +func (p btreeDataPage) setValue(a btreeStore, index int, value []byte) (err error) { + return p.setContent(a, 15+kKV+2*kKV*index, value) +} + +func (p btreeDataPage) cmp(a btreeStore, c func(a, b []byte) int, keyA []byte, keyBIndex int) (y int, err error) { + var keyB []byte + if keyB, err = p.content(a, 15+2*kKV*keyBIndex); err != nil { + return + } + + return c(keyA, keyB), nil +} + +func (p btreeDataPage) copy(src btreeDataPage, di, si, n int) { + do, so := 15+2*kKV*di, 15+2*kKV*si + copy(p[do:do+2*kKV*n], src[so:]) +} + +// {p,left} dirty on exit +func (p btreeDataPage) moveLeft(left btreeDataPage, n int) (btreeDataPage, btreeDataPage) { + nl, np := left.len(), p.len() + left = left.setLen(nl + n) + left.copy(p, nl, 0, n) + p.copy(p, 0, n, np-n) + return p.setLen(np - n), left +} + +func (p btreeDataPage) moveRight(right btreeDataPage, n int) (btreeDataPage, btreeDataPage) { + nr, np := right.len(), p.len() + right = right.setLen(nr + n) + right.copy(right, n, 0, nr) + right.copy(p, 0, np-n, n) + return p.setLen(np - n), right +} + +func (p btreeDataPage) insertItem(a btreeStore, index int, key, value []byte) (btreeDataPage, error) { + p = p.insert(index) + di, sz := 15+2*kKV*index, 2*kKV + copy(p[di:di+sz], zeros[:sz]) + if err := p.setKey(a, index, key); err != nil { + return nil, err + } + return p, p.setValue(a, index, value) +} + +func (p btreeDataPage) split(a btreeStore, root, ph, parent int64, parentIndex, index int, key, value []byte) (btreeDataPage, error) { + right, rh, err := newBTreeDataPageAlloc(a) + // fails defer bufs.GCache.Put(right) + if err != nil { + return nil, err + } + + if next := p.next(); next != 0 { + right.setNext(p.next()) + nxh := right.next() + nx := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(nx) + if nx, err = a.Get(nx, nxh); err != nil { + return nil, err + } + + btreeDataPage(nx).setPrev(rh) + if err = a.Realloc(nxh, nx); err != nil { + return nil, err + } + } + + p.setNext(rh) + right.setPrev(ph) + right = right.setLen(kData) + right.copy(p, 0, kData, kData) + p = p.setLen(kData) + + if parentIndex >= 0 { + var pp btreeIndexPage = bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(pp) + if pp, err = a.Get(pp, parent); err != nil { + return nil, err + } + + pp = pp.insert3(parentIndex, rh, rh) + if err = a.Realloc(parent, pp); err != nil { + return nil, err + } + + } else { + nr := newBTreeIndexPage(ph) + defer bufs.GCache.Put(nr) + nr = nr.insert3(0, rh, rh) + nrh, err := a.Alloc(nr) + if err != nil { + return nil, err + } + + if err = a.Realloc(root, h2b(make([]byte, 7), nrh)); err != nil { + return nil, err + } + + } + if index > kData { + if right, err = right.insertItem(a, index-kData, key, value); err != nil { + return nil, err + } + } else { + if p, err = p.insertItem(a, index, key, value); err != nil { + return nil, err + } + } + if err = a.Realloc(ph, p); err != nil { + return nil, err + } + + return p, a.Realloc(rh, right) +} + +func (p btreeDataPage) overflow(a btreeStore, root, ph, parent int64, parentIndex, index int, key, value []byte) (btreeDataPage, error) { + leftH, rightH, err := checkSiblings(a, parent, parentIndex) + if err != nil { + return nil, err + } + + if leftH != 0 { + left := btreeDataPage(bufs.GCache.Get(maxBuf)) + defer bufs.GCache.Put(left) + if left, err = a.Get(left, leftH); err != nil { + return nil, err + } + + if left.len() < 2*kData { + + p, left = p.moveLeft(left, 1) + if err = a.Realloc(leftH, left); err != nil { + return nil, err + } + + if p, err = p.insertItem(a, index-1, key, value); err != nil { + return nil, err + } + + return p, a.Realloc(ph, p) + } + } + + if rightH != 0 { + right := btreeDataPage(bufs.GCache.Get(maxBuf)) + defer bufs.GCache.Put(right) + if right, err = a.Get(right, rightH); err != nil { + return nil, err + } + + if right.len() < 2*kData { + if index < 2*kData { + p, right = p.moveRight(right, 1) + if err = a.Realloc(rightH, right); err != nil { + return nil, err + } + + if p, err = p.insertItem(a, index, key, value); err != nil { + return nil, err + } + + return p, a.Realloc(ph, p) + } else { + if right, err = right.insertItem(a, 0, key, value); err != nil { + return nil, err + } + + return p, a.Realloc(rightH, right) + } + } + } + return p.split(a, root, ph, parent, parentIndex, index, key, value) +} + +func (p btreeDataPage) swap(a btreeStore, di int, value []byte, canOverwrite bool) (oldValue []byte, err error) { + if oldValue, err = p.value(a, di); err != nil { + return + } + + if !canOverwrite { + return + } + + oldValue = append([]byte(nil), oldValue...) + err = p.setValue(a, di, value) + return +} + +type btreePage []byte + +func (p btreePage) isIndex() bool { + return p[0] == tagBTreeIndexPage +} + +func (p btreePage) len() int { + if p.isIndex() { + return btreeIndexPage(p).len() + } + + return btreeDataPage(p).len() +} + +func (p btreePage) find(a btreeStore, c func(a, b []byte) int, key []byte) (index int, ok bool, err error) { + l := 0 + h := p.len() - 1 + isIndex := p.isIndex() + if c == nil { + c = bytes.Compare + } + for l <= h { + index = (l + h) >> 1 + var cmp int + if isIndex { + if cmp, err = btreeIndexPage(p).cmp(a, c, key, index); err != nil { + return + } + } else { + if cmp, err = btreeDataPage(p).cmp(a, c, key, index); err != nil { + return + } + } + switch ok = cmp == 0; { + case cmp > 0: + l = index + 1 + case ok: + return + default: + h = index - 1 + } + } + return l, false, nil +} + +// p is dirty after extract! +func (p btreeDataPage) extract(a btreeStore, index int) (btreeDataPage, []byte, error) { + value, err := p.valueCopy(a, index) + if err != nil { + return nil, nil, err + } + + if _, h := p.keyField(index); h != 0 { + if err = a.Free(h); err != nil { + return nil, nil, err + } + } + + if _, h := p.valueField(index); h != 0 { + if err = a.Free(h); err != nil { + return nil, nil, err + } + } + + n := p.len() - 1 + if index < n { + p.copy(p, index, index+1, n-index) + } + return p.setLen(n), value, nil +} + +func checkSiblings(a btreeStore, parent int64, parentIndex int) (left, right int64, err error) { + if parentIndex >= 0 { + var p btreeIndexPage = bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(p) + if p, err = a.Get(p, parent); err != nil { + return + } + + if parentIndex > 0 { + left = p.child(parentIndex - 1) + } + if parentIndex < p.len() { + right = p.child(parentIndex + 1) + } + } + return +} + +// underflow must persist all changes made. +func (p btreeDataPage) underflow(a btreeStore, root, iroot, parent, ph int64, parentIndex int) (err error) { + lh, rh, err := checkSiblings(a, parent, parentIndex) + if err != nil { + return err + } + + if lh != 0 { + left := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(left) + if left, err = a.Get(left, lh); err != nil { + return err + } + + if btreeDataPage(left).len()+p.len() >= 2*kData { + left, p = btreeDataPage(left).moveRight(p, 1) + if err = a.Realloc(lh, left); err != nil { + return err + } + + return a.Realloc(ph, p) + } + } + + if rh != 0 { + right := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(right) + if right, err = a.Get(right, rh); err != nil { + return err + } + + if p.len()+btreeDataPage(right).len() > 2*kData { + right, p = btreeDataPage(right).moveLeft(p, 1) + if err = a.Realloc(rh, right); err != nil { + return err + } + + return a.Realloc(ph, p) + } + } + + if lh != 0 { + left := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(left) + if left, err = a.Get(left, lh); err != nil { + return err + } + + if err = a.Realloc(ph, p); err != nil { + return err + } + + return btreeDataPage(left).concat(a, root, iroot, parent, lh, ph, parentIndex-1) + } + + return p.concat(a, root, iroot, parent, ph, rh, parentIndex) +} + +// concat must persist all changes made. +func (p btreeDataPage) concat(a btreeStore, root, iroot, parent, ph, rh int64, parentIndex int) (err error) { + right := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(right) + if right, err = a.Get(right, rh); err != nil { + return err + } + + right, p = btreeDataPage(right).moveLeft(p, btreeDataPage(right).len()) + nxh := btreeDataPage(right).next() + if nxh != 0 { + nx := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(nx) + if nx, err = a.Get(nx, nxh); err != nil { + return err + } + + btreeDataPage(nx).setPrev(ph) + if err = a.Realloc(nxh, nx); err != nil { + return err + } + } + p.setNext(nxh) + if err = a.Free(rh); err != nil { + return err + } + + pp := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(pp) + if pp, err = a.Get(pp, parent); err != nil { + return err + } + + if btreeIndexPage(pp).len() > 1 { + pp = btreeIndexPage(pp).extract(parentIndex) + btreeIndexPage(pp).setChild(parentIndex, ph) + if err = a.Realloc(parent, pp); err != nil { + return err + } + + return a.Realloc(ph, p) + } + + if err = a.Free(iroot); err != nil { + return err + } + + if err = a.Realloc(ph, p); err != nil { + return err + } + + var b7 [7]byte + return a.Realloc(root, h2b(b7[:], ph)) +} + +// external "root" is stable and contains the real root. +type btree int64 + +func newBTree(a btreeStore) (btree, error) { + r, err := a.Alloc(zeros[:7]) + return btree(r), err +} + +func (root btree) String(a btreeStore) string { + r := bufs.GCache.Get(16) + defer bufs.GCache.Put(r) + r, err := a.Get(r, int64(root)) + if err != nil { + panic(err) + } + + iroot := b2h(r) + m := map[int64]bool{int64(root): true} + + s := []string{fmt.Sprintf("tree %#x -> %#x\n====", root, iroot)} + if iroot == 0 { + return s[0] + } + + var f func(int64, string) + f = func(h int64, ind string) { + if m[h] { + return + } + + m[h] = true + var b btreePage = bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(b) + var err error + if b, err = a.Get(b, h); err != nil { + panic(err) + } + + s = append(s, fmt.Sprintf("%s@%#x", ind, h)) + switch b.isIndex() { + case true: + da := []int64{} + b := btreeIndexPage(b) + for i := 0; i < b.len(); i++ { + c, d := b.child(i), b.dataPage(i) + s = append(s, fmt.Sprintf("%schild[%d] %#x dataPage[%d] %#x", ind, i, c, i, d)) + da = append(da, c) + da = append(da, d) + } + i := b.len() + c := b.child(i) + s = append(s, fmt.Sprintf("%schild[%d] %#x", ind, i, c)) + for _, c := range da { + f(c, ind+" ") + } + f(c, ind+" ") + case false: + b := btreeDataPage(b) + s = append(s, fmt.Sprintf("%sprev %#x next %#x", ind, b.prev(), b.next())) + for i := 0; i < b.len(); i++ { + k, err := b.key(a, i) + if err != nil { + panic(err) + } + + v, err := b.value(a, i) + if err != nil { + panic(err) + } + + s = append(s, fmt.Sprintf("%sK[%d]|% x| V[%d]|% x|", ind, i, k, i, v)) + } + } + } + + f(int64(iroot), "") + return strings.Join(s, "\n") +} + +func (root btree) put(dst []byte, a btreeStore, c func(a, b []byte) int, key, value []byte, canOverwrite bool) (prev []byte, err error) { + prev, _, err = root.put2(dst, a, c, key, func(key, old []byte) (new []byte, write bool, err error) { + new, write = value, true + return + }) + return +} + +func (root btree) put2(dst []byte, a btreeStore, c func(a, b []byte) int, key []byte, upd func(key, old []byte) (new []byte, write bool, err error)) (old []byte, written bool, err error) { + var r, value []byte + if r, err = a.Get(dst, int64(root)); err != nil { + return + } + + iroot := b2h(r) + var h int64 + if iroot == 0 { + p := newBTreeDataPage() + defer bufs.GCache.Put(p) + if value, written, err = upd(key, nil); err != nil || !written { + return + } + + if p, err = p.insertItem(a, 0, key, value); err != nil { + return + } + + h, err = a.Alloc(p) + if err != nil { + return nil, true, err + } + + err = a.Realloc(int64(root), h2b(r, h)[:7]) + return + } + + parentIndex := -1 + var parent int64 + ph := iroot + + p := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(p) + + for { + if p, err = a.Get(p[:cap(p)], ph); err != nil { + return + } + + var index int + var ok bool + + if index, ok, err = btreePage(p).find(a, c, key); err != nil { + return + } + + switch { + case ok: // Key found + if btreePage(p).isIndex() { + ph = btreeIndexPage(p).dataPage(index) + if p, err = a.Get(p, ph); err != nil { + return + } + + if old, err = btreeDataPage(p).valueCopy(a, 0); err != nil { + return + } + + if value, written, err = upd(key, old); err != nil || !written { + return + } + + if _, err = btreeDataPage(p).swap(a, 0, value, true); err != nil { + return + } + + err = a.Realloc(ph, p) + return + } + + if old, err = btreeDataPage(p).valueCopy(a, index); err != nil { + return + } + + if value, written, err = upd(key, old); err != nil || !written { + return + } + + if _, err = btreeDataPage(p).swap(a, index, value, true); err != nil { + return + } + + err = a.Realloc(ph, p) + return + case btreePage(p).isIndex(): + if btreePage(p).len() > 2*kIndex { + if p, err = btreeIndexPage(p).split(a, root, &ph, parent, parentIndex, &index); err != nil { + return + } + } + parentIndex = index + parent = ph + ph = btreeIndexPage(p).child(index) + default: + if value, written, err = upd(key, nil); err != nil || !written { + return + } + + if btreePage(p).len() < 2*kData { // page is not full + if p, err = btreeDataPage(p).insertItem(a, index, key, value); err != nil { + return + } + + err = a.Realloc(ph, p) + return + } + + // page is full + p, err = btreeDataPage(p).overflow(a, int64(root), ph, parent, parentIndex, index, key, value) + return + } + } +} + +//TODO actually use 'dst' to return 'value' +func (root btree) get(a btreeStore, dst []byte, c func(a, b []byte) int, key []byte) (b []byte, err error) { + var r []byte + if r, err = a.Get(dst, int64(root)); err != nil { + return + } + + iroot := b2h(r) + if iroot == 0 { + return + } + + ph := iroot + + for { + var p btreePage + if p, err = a.Get(p, ph); err != nil { + return + } + + var index int + var ok bool + if index, ok, err = p.find(a, c, key); err != nil { + return + } + + switch { + case ok: + if p.isIndex() { + dh := btreeIndexPage(p).dataPage(index) + dp, err := a.Get(dst, dh) + if err != nil { + return nil, err + } + + return btreeDataPage(dp).value(a, 0) + } + + return btreeDataPage(p).value(a, index) + case p.isIndex(): + ph = btreeIndexPage(p).child(index) + default: + return + } + } +} + +//TODO actually use 'dst' to return 'value' +func (root btree) extract(a btreeStore, dst []byte, c func(a, b []byte) int, key []byte) (value []byte, err error) { + var r []byte + if r, err = a.Get(dst, int64(root)); err != nil { + return + } + + iroot := b2h(r) + if iroot == 0 { + return + } + + ph := iroot + parentIndex := -1 + var parent int64 + + p := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(p) + + for { + if p, err = a.Get(p[:cap(p)], ph); err != nil { + return + } + + var index int + var ok bool + if index, ok, err = btreePage(p).find(a, c, key); err != nil { + return + } + + if ok { + if btreePage(p).isIndex() { + dph := btreeIndexPage(p).dataPage(index) + dp, err := a.Get(dst, dph) + if err != nil { + return nil, err + } + + if btreeDataPage(dp).len() > kData { + if dp, value, err = btreeDataPage(dp).extract(a, 0); err != nil { + return nil, err + } + + return value, a.Realloc(dph, dp) + } + + if btreeIndexPage(p).len() < kIndex && ph != iroot { + var err error + if p, err = btreeIndexPage(p).underflow(a, int64(root), iroot, parent, &ph, parentIndex, &index); err != nil { + return nil, err + } + } + parentIndex = index + 1 + parent = ph + ph = btreeIndexPage(p).child(parentIndex) + continue + } + + p, value, err = btreeDataPage(p).extract(a, index) + if btreePage(p).len() >= kData { + err = a.Realloc(ph, p) + return + } + + if ph != iroot { + err = btreeDataPage(p).underflow(a, int64(root), iroot, parent, ph, parentIndex) + return + } + + if btreePage(p).len() == 0 { + if err = a.Free(ph); err != nil { + return + } + + err = a.Realloc(int64(root), zeros[:7]) + return + } + err = a.Realloc(ph, p) + return + } + + if !btreePage(p).isIndex() { + return + } + + if btreePage(p).len() < kIndex && ph != iroot { + if p, err = btreeIndexPage(p).underflow(a, int64(root), iroot, parent, &ph, parentIndex, &index); err != nil { + return nil, err + } + } + parentIndex = index + parent = ph + ph = btreeIndexPage(p).child(index) + } +} + +func (root btree) deleteAny(a btreeStore) (bool, error) { + r := bufs.GCache.Get(7) + defer bufs.GCache.Put(r) + var err error + if r, err = a.Get(r, int64(root)); err != nil { + return false, err + } + + iroot := b2h(r) + if iroot == 0 { + return true, nil + } + + ph := iroot + parentIndex := -1 + var parent int64 + p := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(p) + + for { + if p, err = a.Get(p, ph); err != nil { + return false, err + } + + index := btreePage(p).len() / 2 + if btreePage(p).isIndex() { + dph := btreeIndexPage(p).dataPage(index) + dp := bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(dp) + if dp, err = a.Get(dp, dph); err != nil { + return false, err + } + + if btreeDataPage(dp).len() > kData { + if dp, _, err = btreeDataPage(dp).extract(a, 0); err != nil { + return false, err + } + + return false, a.Realloc(dph, dp) + } + + if btreeIndexPage(p).len() < kIndex && ph != iroot { + if p, err = btreeIndexPage(p).underflow(a, int64(root), iroot, parent, &ph, parentIndex, &index); err != nil { + return false, err + } + } + parentIndex = index + 1 + parent = ph + ph = btreeIndexPage(p).child(parentIndex) + continue + } + + p, _, err = btreeDataPage(p).extract(a, index) + if btreePage(p).len() >= kData { + err = a.Realloc(ph, p) + return false, err + } + + if ph != iroot { + err = btreeDataPage(p).underflow(a, int64(root), iroot, parent, ph, parentIndex) + return false, err + } + + if btreePage(p).len() == 0 { + if err = a.Free(ph); err != nil { + return true, err + } + + return true, a.Realloc(int64(root), zeros[:7]) + } + + return false, a.Realloc(ph, p) + } +} + +func (root btree) first(a btreeStore) (ph int64, p btreeDataPage, err error) { + r := bufs.GCache.Get(7) + defer bufs.GCache.Put(r) + if r, err = a.Get(r, int64(root)); err != nil { + return + } + + for ph = b2h(r); ph != 0; ph = btreeIndexPage(p).child(0) { + if p, err = a.Get(p, ph); err != nil { + return + } + + if !btreePage(p).isIndex() { + break + } + } + + return +} + +func (root btree) last(a btreeStore) (ph int64, p btreeDataPage, err error) { + r := bufs.GCache.Get(7) + defer bufs.GCache.Put(r) + if r, err = a.Get(r, int64(root)); err != nil { + return + } + + for ph = b2h(r); ph != 0; ph = btreeIndexPage(p).child(btreeIndexPage(p).len()) { + if p, err = a.Get(p, ph); err != nil { + return + } + + if !btreePage(p).isIndex() { + break + } + } + + return +} + +// key >= p[index].key +func (root btree) seek(a btreeStore, c func(a, b []byte) int, key []byte) (p btreeDataPage, index int, equal bool, err error) { + r := bufs.GCache.Get(7) + defer bufs.GCache.Put(r) + if r, err = a.Get(r, int64(root)); err != nil { + return + } + + for ph := b2h(r); ph != 0; ph = btreeIndexPage(p).child(index) { + if p, err = a.Get(p, ph); err != nil { + break + } + + if index, equal, err = btreePage(p).find(a, c, key); err != nil { + break + } + + if equal { + if !btreePage(p).isIndex() { + break + } + + p, err = a.Get(p, btreeIndexPage(p).dataPage(index)) + index = 0 + break + } + + if !btreePage(p).isIndex() { + break + } + } + return +} + +func (root btree) clear(a btreeStore) (err error) { + r := bufs.GCache.Get(7) + defer bufs.GCache.Put(r) + if r, err = a.Get(r, int64(root)); err != nil { + return + } + + iroot := b2h(r) + if iroot == 0 { + return + } + + if err = root.clear2(a, iroot); err != nil { + return + } + + var b [7]byte + return a.Realloc(int64(root), b[:]) +} + +func (root btree) clear2(a btreeStore, ph int64) (err error) { + var p = bufs.GCache.Get(maxBuf) + defer bufs.GCache.Put(p) + if p, err = a.Get(p, ph); err != nil { + return + } + + switch btreePage(p).isIndex() { + case true: + ip := btreeIndexPage(p) + for i := 0; i <= ip.len(); i++ { + root.clear2(a, ip.child(i)) + + } + case false: + dp := btreeDataPage(p) + for i := 0; i < dp.len(); i++ { + if err = dp.setKey(a, i, nil); err != nil { + return + } + + if err = dp.setValue(a, i, nil); err != nil { + return + } + } + } + return a.Free(ph) +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/btree_test.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/btree_test.go new file mode 100644 index 000000000..06c4ae042 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/btree_test.go @@ -0,0 +1,1887 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lldb + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "flag" + "fmt" + "math" + "math/rand" + "os" + "runtime" + "testing" + + "github.com/cznic/fileutil" + "github.com/cznic/mathutil" +) + +var ( + testFrom = flag.Uint("from", 0, "test I [-from, -N)") + noGrow = flag.Bool("noGrow", false, "check only embeded keys/values") +) + +func verifyPageLinks(a btreeStore, tree btree, n int) (err error) { + var p btreeDataPage + var ph int64 + if ph, p, err = tree.first(a); err != nil { + return + } + + if n == 0 { + if ph != 0 || p != nil { + return fmt.Errorf("first() should returned nil page") + } + + ph2, p2, err := tree.last(a) + if err != nil { + return err + } + + if ph2 != 0 || p2 != nil { + return fmt.Errorf("last() should returned nil page") + } + + } + + n0 := n + var prev int64 + var lastKey []byte + for ph != 0 { + if p, err = a.Get(nil, ph); err != nil { + return + } + + if g, e := p.prev(), prev; g != e { + return fmt.Errorf("broken L-R DLL chain: p %d p.prev %d, exp %d", ph, g, e) + } + + for i := 0; i < p.len(); i++ { + key, err := p.key(a, i) + if err != nil { + return err + } + + if key == nil { + return fmt.Errorf("nil key") + } + + if lastKey != nil && !(bytes.Compare(lastKey, key) < 0) { + return fmt.Errorf("L-R key ordering broken") + } + + lastKey = key + n-- + } + + prev, ph = ph, p.next() + } + + if n != 0 { + return fmt.Errorf("# of keys off by %d (L-R)", n) + } + + n = n0 + if ph, p, err = tree.last(a); err != nil { + return + } + + lastKey = nil + var next int64 + + for ph != 0 { + if p, err = a.Get(nil, ph); err != nil { + return + } + + if g, e := p.next(), next; g != e { + return fmt.Errorf("broken R-L DLL chain") + } + + for i := p.len() - 1; i >= 0; i-- { + key, err := p.key(a, i) + if err != nil { + return err + } + + if key == nil { + return fmt.Errorf("nil key") + } + + if lastKey != nil && !(bytes.Compare(key, lastKey) < 0) { + return fmt.Errorf("R-L key ordering broken") + } + + lastKey = key + n-- + } + + next, ph = ph, p.prev() + } + + if n != 0 { + return fmt.Errorf("# of keys off by %d (R-L)", n) + } + + return +} + +func testBTreePut1(t *testing.T, nf func() btreeStore, grow, from, to, xor int64) (tree btree) { + if *noGrow { + grow = 0 + } + + a := nf() + if a == nil { + t.Fatal(a) + } + + var err error + tree, err = newBTree(a) + if err != nil { + t.Fatal(err) + } + + if err := verifyPageLinks(a, tree, 0); err != nil { + t.Fatal(err) + } + + // Write and read back + var k, v, prevValue [7]byte + + n := 0 + for i := from; i < to; i++ { + h2b(k[:], 0x0100000000+i^xor) + h2b(v[:], 0x0200000000+i^xor) + kk := append(make([]byte, grow*i), k[:]...) + vv := append(make([]byte, grow*i), v[:]...) + prev, err := tree.put(nil, a, nil, kk, vv, true) + if err != nil || len(prev) != 0 { + t.Fatal(i, prev, err) + } + + var buf []byte + if buf, err = tree.get(a, nil, nil, kk); err != nil { + t.Fatal(err) + } + + if !bytes.Equal(buf, vv) { + t.Fatalf("\nK %sG %sE %s%s", hex.Dump(kk), hex.Dump(buf), hex.Dump(vv), tree.String(a)) + } + + n++ + } + + if err := verifyPageLinks(a, tree, n); err != nil { + t.Fatalf("%s\n%s", err, tree.String(a)) + } + + // Overwrite, read and extract + for i := from; i < to; i++ { + h2b(k[:], 0x0100000000+i^xor) + h2b(prevValue[:], 0x0200000000+i^xor) + h2b(v[:], 0x0300000000+i^xor) + kk := append(make([]byte, grow*i), k[:]...) + vv := append(make([]byte, grow*i), v[:]...) + expPrev := append(make([]byte, grow*i), prevValue[:]...) + gotPrev, err := tree.put(nil, a, nil, kk, vv, true) + if err != nil { + t.Fatal(i, err) + } + + if !bytes.Equal(gotPrev, expPrev) { + t.Fatalf("\nK %sG %sE %s%s", hex.Dump(kk), hex.Dump(gotPrev), hex.Dump(expPrev), tree.String(a)) + } + + var buf []byte + if buf, err = tree.get(a, nil, nil, kk); err != nil { + t.Fatal(err) + } + + if !bytes.Equal(buf, vv) { + t.Fatalf("\n%s%s%s%s", hex.Dump(kk), hex.Dump(buf), hex.Dump(vv), tree.String(a)) + } + + buf = nil + if buf, err = tree.extract(a, nil, nil, kk); err != nil { + t.Fatal(err) + } + + if !bytes.Equal(buf, vv) { + t.Fatalf("i %d, from [%d, %d)\nK %sG %sE %s%s", i, from, to, hex.Dump(kk), hex.Dump(buf), hex.Dump(vv), tree.String(a)) + } + + buf = nil + if buf, err = tree.get(a, nil, nil, kk); err != nil { + t.Fatal(err) + } + + if buf != nil { + t.Fatalf("\nK %sB %s%s", hex.Dump(kk), hex.Dump(buf), tree.String(a)) + } + + buf = nil + if buf, err = tree.extract(a, nil, nil, kk); err != nil { + t.Fatal(err) + } + + if buf != nil { + t.Fatalf("\n%s\n%s%s", hex.Dump(kk), hex.Dump(buf), tree.String(a)) + } + + n-- + if err := verifyPageLinks(a, tree, n); err != nil { + t.Fatalf("%s\n%s", err, tree.String(a)) + } + } + + return +} + +var xors = [...]int64{0, 0xffffffff, 0x55555555, 0xaaaaaaaa} + +func TestBTreePutGetExtract(t *testing.T) { + N := int64(3 * kData) + from := int64(*testFrom) + + for grow := 0; grow < 2; grow++ { + for _, x := range xors { + var s *memBTreeStore + tree := testBTreePut1(t, func() btreeStore { s = newMemBTreeStore(); return s }, int64(grow), from, N, x) + if err := verifyPageLinks(s, tree, 0); err != nil { + t.Fatal(err) + } + + if g, e := len(s.m), 1; g != e { + t.Fatalf("leak(s) %d %d\n%s", g, e, s) + } + } + } +} + +func testBTreePut2(t *testing.T, nf func() btreeStore, grow, n int) (tree btree) { + if *noGrow { + grow = 0 + } + rng, err := mathutil.NewFC32(math.MinInt32, math.MaxInt32, true) + if err != nil { + t.Fatal(err) + } + + a := nf() + if a == nil { + t.Fatal(a) + } + + tree, err = newBTree(a) + if err != nil { + t.Fatal(err) + } + + var k, v [7]byte + for i := 0; i < n; i++ { + ik, iv := int64(rng.Next()), int64(rng.Next()) + h2b(k[:], ik) + h2b(v[:], iv) + kk := append(make([]byte, grow*i), k[:]...) + vv := append(make([]byte, grow*i), v[:]...) + prev, err := tree.put(nil, a, nil, kk, vv, true) + if err != nil || len(prev) != 0 { + t.Fatal(i, prev, err) + } + + var buf []byte + if buf, err = tree.get(a, nil, nil, kk); err != nil { + t.Fatal(err) + } + + if !bytes.Equal(buf, vv) { + t.Fatalf("\n%s%s%s%s", hex.Dump(kk), hex.Dump(buf), hex.Dump(vv), tree.String(a)) + } + } + + if err := verifyPageLinks(a, tree, n); err != nil { + t.Fatalf("%s\n%s\n", err, tree.String(a)) + } + + rng.Seek(0) + for i := 0; i < n; i++ { + ik, iv := int64(rng.Next()), int64(rng.Next()) + h2b(k[:], ik) + h2b(v[:], iv) + kk := append(make([]byte, grow*i), k[:]...) + vv := append(make([]byte, grow*i), v[:]...) + var buf []byte + buf, err := tree.extract(a, nil, nil, kk) + if err != nil { + t.Fatal(i, err) + } + + if !bytes.Equal(buf, vv) { + t.Fatalf("\n%s\n%s\n%s\n%s", hex.Dump(kk), hex.Dump(buf), hex.Dump(vv), tree.String(a)) + } + + if err := verifyPageLinks(a, tree, n-i-1); err != nil { + t.Fatalf("%s\n%s", err, tree.String(a)) + } + } + + return +} + +func TestBTreePutGetExtractRnd(t *testing.T) { + N := *testN + + for grow := 0; grow < 2; grow++ { + var s *memBTreeStore + tree := testBTreePut2(t, func() btreeStore { s = newMemBTreeStore(); return s }, grow, N) + if err := verifyPageLinks(s, tree, 0); err != nil { + t.Fatal(err) + } + + if g, e := len(s.m), 1; g != e { + t.Fatalf("leak(s) %d %d\n%s", g, e, s) + } + } +} + +func benchmarkBTreePut(b *testing.B, v []byte) { + b.StopTimer() + rng := rand.New(rand.NewSource(42)) + ka := make([][7]byte, b.N) + for _, v := range ka { + h2b(v[:], int64(rng.Int63())) + } + a := newMemBTreeStore() + tree, err := newBTree(a) + if err != nil { + b.Fatal(err) + } + + runtime.GC() + b.StartTimer() + for _, k := range ka { + tree.put(nil, a, bytes.Compare, k[:], v, true) + } +} + +func BenchmarkBTreePut1(b *testing.B) { + v := make([]byte, 1) + benchmarkBTreePut(b, v) +} + +func BenchmarkBTreePut8(b *testing.B) { + v := make([]byte, 8) + benchmarkBTreePut(b, v) +} + +func BenchmarkBTreePut16(b *testing.B) { + v := make([]byte, 16) + benchmarkBTreePut(b, v) +} + +func BenchmarkBTreePut32(b *testing.B) { + v := make([]byte, 32) + benchmarkBTreePut(b, v) +} + +func benchmarkBTreeGet(b *testing.B, v []byte) { + b.StopTimer() + rng := rand.New(rand.NewSource(42)) + ka := make([][7]byte, b.N) + for _, v := range ka { + h2b(v[:], int64(rng.Int63())) + } + a := newMemBTreeStore() + tree, err := newBTree(a) + if err != nil { + b.Fatal(err) + } + + for _, k := range ka { + tree.put(nil, a, bytes.Compare, k[:], v, true) + } + buf := make([]byte, len(v)) + runtime.GC() + b.StartTimer() + for _, k := range ka { + tree.get(a, buf, bytes.Compare, k[:]) + } +} + +func BenchmarkBTreeGet1(b *testing.B) { + v := make([]byte, 1) + benchmarkBTreeGet(b, v) +} + +func BenchmarkBTreeGet8(b *testing.B) { + v := make([]byte, 8) + benchmarkBTreeGet(b, v) +} + +func BenchmarkBTreeGet16(b *testing.B) { + v := make([]byte, 16) + benchmarkBTreeGet(b, v) +} + +func BenchmarkBTreeGet32(b *testing.B) { + v := make([]byte, 32) + benchmarkBTreeGet(b, v) +} + +func TestbTreeSeek(t *testing.T) { + N := int64(*testN) + + tree := NewBTree(nil) + + // Fill + for i := int64(1); i <= N; i++ { + tree.Set(enc8(10*i), enc8(10*i+1)) + } + + // Check + a, root := tree.store, tree.root + for i := int64(1); i <= N; i++ { + // Exact match + lowKey := enc8(10*i - 1) + key := enc8(10 * i) + highKey := enc8(10*i + 1) + p, index, eq, err := root.seek(a, nil, key) + if err != nil { + t.Fatal(err) + } + + if !eq { + t.Fatal(i) + } + + if btreePage(p).isIndex() { + t.Fatal(i, "need btreeDataPage") + } + + dp := btreeDataPage(p) + n := dp.len() + if n < 0 || n > 2*kData { + t.Fatal(i, n) + } + + if index < 0 || index >= n { + t.Fatal(index) + } + + g, err := dp.key(a, index) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(g, key) { + t.Fatal(i) + } + + g, err = dp.value(a, index) + if err != nil { + t.Fatal(err) + } + + value := enc8(10*i + 1) + if !bytes.Equal(g, value) { + t.Fatal(i) + } + + // Nonexistent "low" key. Search for 9 should return the key 10. + p, index, eq, err = root.seek(a, nil, lowKey) + if err != nil { + t.Fatal(err) + } + + if eq { + t.Fatal(i) + } + + if btreePage(p).isIndex() { + t.Fatal(i, "need btreeDataPage") + } + + dp = btreeDataPage(p) + n = dp.len() + if n < 0 || n > 2*kData { + t.Fatal(i, n) + } + + if index < 0 || index > n { + t.Fatal(index, n) + } + + if index == n { + ph := dp.next() + index = 0 + if dp, err = a.Get(p, ph); err != nil { + t.Fatal(err) + } + } + + g, err = dp.key(a, index) + if err != nil { + t.Fatal(err) + } + + expKey := key + if !bytes.Equal(g, expKey) { + fmt.Println(root.String(a)) + //t.Fatalf("%d low|% x| g|% x| e|% x|", i, lowKey, g, expKey) + } + + g, err = dp.value(a, index) + if err != nil { + t.Fatal(err) + } + + value = enc8(10*i + 1) + if !bytes.Equal(g, value) { + t.Fatal(i) + } + + // Nonexistent "high" key. Search for 11 should return the key 20. + p, index, eq, err = root.seek(a, nil, highKey) + if err != nil { + t.Fatal(err) + } + + if eq { + t.Fatal(i) + } + + if btreePage(p).isIndex() { + t.Fatal(i, "need btreeDataPage") + } + + dp = btreeDataPage(p) + n = dp.len() + if n < 0 || n > 2*kData { + t.Fatal(i, n) + } + + if index < 0 || index > n { + t.Fatal(index, n) + } + + if index == n { + ph := dp.next() + if i == N { + if ph != 0 { + t.Fatal(ph) + } + + continue + } + + index = 0 + if dp, err = a.Get(p, ph); err != nil { + t.Fatal(err) + } + } + + g, err = dp.key(a, index) + if err != nil { + t.Fatal(err) + } + + expKey = enc8(10 * (i + 1)) + if !bytes.Equal(g, expKey) { + //fmt.Println(root.String(a)) + t.Fatalf("%d low|% x| g|% x| e|% x|", i, lowKey, g, expKey) + } + + g, err = dp.value(a, index) + if err != nil { + t.Fatal(err) + } + + value = enc8(10*(i+1) + 1) + if !bytes.Equal(g, value) { + t.Fatal(i) + } + + } +} + +func enc8(n int64) []byte { + var b [8]byte + h2b(b[:], n) + return b[:] +} + +func dec8(b []byte) (int64, error) { + if len(b) != 0 { + return 0, fmt.Errorf("dec8: len != 8 but %d", len(b)) + } + + return b2h(b), nil +} + +func TestbTreeNext(t *testing.T) { + N := int64(*testN) + + tree := NewBTree(nil) + enum, _, err := tree.seek(enc8(0)) + if err != nil { + t.Fatal(err) + } + + if _, _, err = enum.current(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + + if err = enum.next(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + + if err = enum.prev(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + + // Fill + for i := int64(1); i <= N; i++ { + tree.Set(enc8(10*i), enc8(10*i+1)) + } + + var eq bool + + enum, eq, err = tree.seek(enc8(0)) + if err != nil { + t.Fatal(err) + } + + if eq { + t.Fatal(eq) + } + + // index: 0 + if _, _, err = enum.current(); err != nil { + t.Fatal(err) + } + + if err = enum.next(); N > 1 && err != nil { + t.Fatal(err) + } + + enum, eq, err = tree.seek(enc8(N * 10)) + if err != nil { + t.Fatal(err) + } + + if !eq { + t.Fatal(eq) + } + + // index: N-1 + if _, _, err = enum.current(); err != nil { + t.Fatal(err) + } + + if err = enum.next(); N > 1 && !fileutil.IsEOF(err) { + t.Fatal(err) + } + + enum, eq, err = tree.seek(enc8(N*10 + 1)) + if err != nil { + t.Fatal(err) + } + + if eq { + t.Fatal(eq) + } + + // index: N + if _, _, err = enum.current(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + + if err = enum.next(); N > 1 && !fileutil.IsEOF(err) { + t.Fatal(err) + } + + enum, _, err = tree.seek(enc8(0)) + if err != nil { + t.Fatal(err) + } + + for i := int64(1); i <= N; i++ { + expKey, expValue := enc8(10*i), enc8(10*i+1) + k, v, err := enum.current() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(k, expKey) { + t.Fatal(i) + } + + if !bytes.Equal(v, expValue) { + t.Fatal(i) + } + + switch { + case i == N: + if err := enum.next(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + default: + if err := enum.next(); err != nil { + t.Fatal(err) + } + } + } +} + +func TestbTreePrev(t *testing.T) { + N := int64(*testN) + + tree := NewBTree(nil) + enum, _, err := tree.seek(enc8(0)) + if err != nil { + t.Fatal(err) + } + + if _, _, err = enum.current(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + + if err = enum.next(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + + if err = enum.prev(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + + // Fill + for i := int64(1); i <= N; i++ { + tree.Set(enc8(10*i), enc8(10*i+1)) + } + + var eq bool + + enum, eq, err = tree.seek(enc8(0)) + if err != nil { + t.Fatal(err) + } + + if eq { + t.Fatal(eq) + } + + // index: 0 + if _, _, err = enum.current(); err != nil { + t.Fatal(err) + } + + if err = enum.prev(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + + enum, eq, err = tree.seek(enc8(N * 10)) + if err != nil { + t.Fatal(err) + } + + if !eq { + t.Fatal(eq) + } + + // index: N-1 + if _, _, err = enum.current(); err != nil { + t.Fatal(err) + } + + if err = enum.prev(); N > 1 && err != nil { + t.Fatal(err) + } + + enum, eq, err = tree.seek(enc8(N*10 + 1)) + if err != nil { + t.Fatal(err) + } + + if eq { + t.Fatal(eq) + } + + // index: N + if _, _, err = enum.current(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + + if err = enum.prev(); err != nil { + t.Fatal(err) + } + + enum, _, err = tree.seek(enc8(N * 10)) + if err != nil { + t.Fatal(err) + } + + for i := N; i >= 1; i-- { + expKey, expValue := enc8(10*i), enc8(10*i+1) + k, v, err := enum.current() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(k, expKey) { + t.Fatalf("%d k|% x| expK|% x| %s\n", i, k, expKey, tree.root.String(tree.store)) + } + + if !bytes.Equal(v, expValue) { + t.Fatal(i) + } + + switch { + case i == 1: + if err := enum.prev(); !fileutil.IsEOF(err) { + t.Fatal(err) + } + default: + if err := enum.prev(); err != nil { + t.Fatal(i, err) + } + } + } +} + +func TestBTreeClear(t *testing.T) { + N := int64(*testN) + + var err error + var p []byte + for n := int64(0); n <= N; n = n*3/2 + 1 { + tree := NewBTree(nil) + for i := int64(0); i < n; i++ { + k := append(make([]byte, kKV), enc8(10*i+1)...) + v := append(make([]byte, kKV+1), enc8(10*i+2)...) + if err = tree.Set(k, v); err != nil { + t.Fatal(err) + } + } + + if err = tree.Clear(); err != nil { + t.Fatal(err) + } + + if g, e := len(tree.store.(*memBTreeStore).m), 1; g != e { + t.Fatalf("%v %v %v\n%s", n, g, e, tree.store.(*memBTreeStore).String()) + } + + if p, err = tree.store.Get(p, 1); err != nil { + t.Fatal(err) + } + + if g, e := p, zeros[:7]; len(g) != 0 && !bytes.Equal(g, e) { + t.Fatalf("|% x| |% x|", g, e) + } + } +} + +func TestBTreeRemove(t *testing.T) { + N := int64(*testN) + + for n := int64(0); n <= N; n = n*3/2 + 1 { + f := NewMemFiler() + store, err := NewAllocator(f, &Options{}) + if err != nil { + t.Fatal(err) + } + + sz0, err := f.Size() + if err != nil { + t.Fatal(err) + } + + tree, handle, err := CreateBTree(store, nil) + if err != nil { + t.Fatal(err) + } + + for i := int64(0); i < n; i++ { + k := append(make([]byte, kKV), enc8(10*i+1)...) + v := append(make([]byte, kKV+1), enc8(10*i+2)...) + if err = tree.Set(k, v); err != nil { + t.Fatal(err) + } + } + + if err = RemoveBTree(store, handle); err != nil { + t.Fatal(err) + } + + sz, err := f.Size() + if err != nil { + t.Fatal(err) + } + + if g, e := sz-sz0, int64(0); g != e { + t.Fatal(g, e) + } + } +} + +func collate(a, b []byte) (r int) { + da, err := DecodeScalars(a) + if err != nil { + panic(err) + } + + db, err := DecodeScalars(b) + if err != nil { + panic(err) + } + + r, err = Collate(da, db, nil) + if err != nil { + panic(err) + } + + return +} + +func TestBTreeCollatingBug(t *testing.T) { + tree := NewBTree(collate) + + date, err := EncodeScalars("Date") + if err != nil { + t.Fatal(err) + } + + customer, err := EncodeScalars("Customer") + if err != nil { + t.Fatal(err) + } + + if g, e := collate(customer, date), -1; g != e { + t.Fatal(g, e) + } + + if g, e := collate(date, customer), 1; g != e { + t.Fatal(g, e) + } + + err = tree.Set(date, nil) + if err != nil { + t.Fatal(err) + } + + err = tree.Set(customer, nil) + if err != nil { + t.Fatal(err) + } + + var b bytes.Buffer + tree.Dump(&b) + t.Logf("\n%s", b.String()) + + key, _, err := tree.First() + if err != nil { + t.Fatal(err) + } + + if g, e := key, customer; !bytes.Equal(g, e) { + t.Fatal(g, e) + } + +} + +func TestExtract(t *testing.T) { // Test of the exported wrapper only, .extract tested elsewhere + bt := NewBTree(nil) + bt.Set([]byte("a"), []byte("b")) + bt.Set([]byte("c"), []byte("d")) + bt.Set([]byte("e"), []byte("f")) + + if v, err := bt.Get(nil, []byte("a")); string(v) != "b" || err != nil { + t.Fatal(v, err) + } + + if v, err := bt.Get(nil, []byte("c")); string(v) != "d" || err != nil { + t.Fatal(v, err) + } + + if v, err := bt.Get(nil, []byte("e")); string(v) != "f" || err != nil { + t.Fatal(v, err) + } + + if v, err := bt.Extract(nil, []byte("c")); string(v) != "d" || err != nil { + t.Fatal(v, err) + } + + if v, err := bt.Get(nil, []byte("a")); string(v) != "b" || err != nil { + t.Fatal(v, err) + } + + if v, err := bt.Get(nil, []byte("c")); v != nil || err != nil { + t.Fatal(v, err) + } + + if v, err := bt.Get(nil, []byte("e")); string(v) != "f" || err != nil { + t.Fatal(v, err) + } +} + +func TestFirst(t *testing.T) { + bt := NewBTree(nil) + + if k, v, err := bt.First(); k != nil || v != nil || err != nil { + t.Fatal(k, v, err) + } + + bt.Set([]byte("a"), []byte("b")) + bt.Set([]byte("c"), []byte("d")) + + if k, v, err := bt.First(); string(k) != "a" || string(v) != "b" || err != nil { + t.Fatal(k, v, err) + } + + if err := bt.Delete([]byte("a")); err != nil { + t.Fatal(err) + } + + if k, v, err := bt.First(); string(k) != "c" || string(v) != "d" || err != nil { + t.Fatal(k, v, err) + } + + if err := bt.Delete([]byte("c")); err != nil { + t.Fatal(err) + } + + if k, v, err := bt.First(); k != nil || v != nil || err != nil { + t.Fatal(k, v, err) + } +} + +func TestLast(t *testing.T) { + bt := NewBTree(nil) + + if k, v, err := bt.First(); k != nil || v != nil || err != nil { + t.Fatal(k, v, err) + } + + bt.Set([]byte("a"), []byte("b")) + bt.Set([]byte("c"), []byte("d")) + + if k, v, err := bt.Last(); string(k) != "c" || string(v) != "d" || err != nil { + t.Fatal(k, v, err) + } + + if err := bt.Delete([]byte("c")); err != nil { + t.Fatal(err) + } + + if k, v, err := bt.First(); string(k) != "a" || string(v) != "b" || err != nil { + t.Fatal(k, v, err) + } + + if err := bt.Delete([]byte("a")); err != nil { + t.Fatal(err) + } + + if k, v, err := bt.First(); k != nil || v != nil || err != nil { + t.Fatal(k, v, err) + } +} + +func TestseekFirst(t *testing.T) { + bt := NewBTree(nil) + + enum, err := bt.seekFirst() + if !fileutil.IsEOF(err) { + t.Fatal(err) + } + + bt.Set([]byte("c"), []byte("d")) + enum, err = bt.seekFirst() + if err != nil { + t.Fatal(err) + } + + err = enum.prev() + if !fileutil.IsEOF(err) { + t.Fatal(err) + } + + err = enum.next() + if !fileutil.IsEOF(err) { + t.Fatal(err) + } + + k, v, err := enum.current() + if err != nil { + t.Fatal(err) + } + + if string(k) != "c" || string(v) != "d" { + t.Fatal(k, v) + } + + bt.Set([]byte("a"), []byte("b")) + enum, err = bt.seekFirst() + if err != nil { + t.Fatal(err) + } + + err = enum.prev() + if !fileutil.IsEOF(err) { + t.Fatal(err) + } + + k, v, err = enum.current() + if err != nil { + t.Fatal(err) + } + + if string(k) != "a" || string(v) != "b" { + t.Fatal(k, v) + } + + err = enum.next() + if err != nil { + t.Fatal(err) + } + + k, v, err = enum.current() + if err != nil { + t.Fatal(err) + } + + if string(k) != "c" || string(v) != "d" { + t.Fatal(k, v) + } +} + +func TestseekLast(t *testing.T) { + bt := NewBTree(nil) + + enum, err := bt.seekFirst() + if !fileutil.IsEOF(err) { + t.Fatal(err) + } + + bt.Set([]byte("a"), []byte("b")) + enum, err = bt.seekFirst() + if err != nil { + t.Fatal(err) + } + + err = enum.prev() + if !fileutil.IsEOF(err) { + t.Fatal(err) + } + + err = enum.next() + if !fileutil.IsEOF(err) { + t.Fatal(err) + } + + k, v, err := enum.current() + if err != nil { + t.Fatal(err) + } + + if string(k) != "a" || string(v) != "b" { + t.Fatal(k, v) + } + + bt.Set([]byte("c"), []byte("d")) + enum, err = bt.seekLast() + if err != nil { + t.Fatal(err) + } + + err = enum.next() + if !fileutil.IsEOF(err) { + t.Fatal(err) + } + + k, v, err = enum.current() + if err != nil { + t.Fatal(err) + } + + if string(k) != "c" || string(v) != "d" { + t.Fatal(k, v) + } + + err = enum.prev() + if err != nil { + t.Fatal(err) + } + + k, v, err = enum.current() + if err != nil { + t.Fatal(err) + } + + if string(k) != "a" || string(v) != "b" { + t.Fatal(k, v) + } +} + +func TestDeleteAny(t *testing.T) { + const N = 1e4 + rng := rand.New(rand.NewSource(42)) + ref := map[uint32]bool{} + tr := NewBTree(nil) + data := []byte{42} + var key [4]byte + for i := 0; i < N; i++ { + k := uint32(rng.Int()) + binary.LittleEndian.PutUint32(key[:], k) + if err := tr.Set(key[:], data); err != nil { + t.Fatal(err) + } + + ref[k] = true + } + + for i := len(ref); i != 0; i-- { + empty, err := tr.DeleteAny() + if err != nil { + t.Fatal(err) + } + + if empty && i != 1 { + t.Fatal(i) + } + } +} + +func benchmarkBTreeSetFiler(b *testing.B, f Filer, sz int) { + if err := f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + a, err := NewAllocator(f, &Options{}) + if err != nil { + b.Error(err) + return + } + + tr, _, err := CreateBTree(a, nil) + if err != nil { + f.EndUpdate() + b.Error(err) + return + } + + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + + keys := make([][8]byte, b.N) + for i := range keys { + binary.BigEndian.PutUint64(keys[i][:], uint64(i)) + } + v := make([]byte, sz) + runtime.GC() + b.ResetTimer() + for _, k := range keys { + if err = f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + if err := tr.Set(k[:], v); err != nil { + f.EndUpdate() + b.Error(err) + return + } + + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + } +} + +func benchmarkBTreeSetMemFiler(b *testing.B, sz int) { + f := NewMemFiler() + benchmarkBTreeSetFiler(b, f, sz) +} + +func BenchmarkBTreeSetMemFiler0(b *testing.B) { + benchmarkBTreeSetMemFiler(b, 0) +} + +func BenchmarkBTreeSetMemFiler1e1(b *testing.B) { + benchmarkBTreeSetMemFiler(b, 1e1) +} + +func BenchmarkBTreeSetMemFiler1e2(b *testing.B) { + benchmarkBTreeSetMemFiler(b, 1e2) +} + +func BenchmarkBTreeSetMemFiler1e3(b *testing.B) { + benchmarkBTreeSetMemFiler(b, 1e3) +} + +func benchmarkBTreeSetSimpleFileFiler(b *testing.B, sz int) { + dir, testDbName := temp() + defer os.RemoveAll(dir) + + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer f.Close() + + benchmarkBTreeSetFiler(b, NewSimpleFileFiler(f), sz) +} + +func BenchmarkBTreeSetSimpleFileFiler0(b *testing.B) { + benchmarkBTreeSetSimpleFileFiler(b, 0) +} + +func BenchmarkBTreeSetSimpleFileFiler1e1(b *testing.B) { + benchmarkBTreeSetSimpleFileFiler(b, 1e1) +} + +func BenchmarkBTreeSetSimpleFileFiler1e2(b *testing.B) { + benchmarkBTreeSetSimpleFileFiler(b, 1e2) +} + +func BenchmarkBTreeSetSimpleFileFiler1e3(b *testing.B) { + benchmarkBTreeSetSimpleFileFiler(b, 1e3) +} + +func benchmarkBTreeSetRollbackFiler(b *testing.B, sz int) { + dir, testDbName := temp() + defer os.RemoveAll(dir) + + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer f.Close() + + g := NewSimpleFileFiler(f) + var filer *RollbackFiler + if filer, err = NewRollbackFiler( + g, + func(sz int64) error { + if err = g.Truncate(sz); err != nil { + return err + } + + return g.Sync() + }, + g, + ); err != nil { + b.Error(err) + return + } + + benchmarkBTreeSetFiler(b, filer, sz) +} + +func BenchmarkBTreeSetRollbackFiler0(b *testing.B) { + benchmarkBTreeSetRollbackFiler(b, 0) +} + +func BenchmarkBTreeSetRollbackFiler1e1(b *testing.B) { + benchmarkBTreeSetRollbackFiler(b, 1e1) +} + +func BenchmarkBTreeSetRollbackFiler1e2(b *testing.B) { + benchmarkBTreeSetRollbackFiler(b, 1e2) +} + +func BenchmarkBTreeSetRollbackFiler1e3(b *testing.B) { + benchmarkBTreeSetRollbackFiler(b, 1e3) +} + +func benchmarkBTreeSetACIDFiler(b *testing.B, sz int) { + dir, testDbName := temp() + defer os.RemoveAll(dir) + + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer f.Close() + + wal, err := os.OpenFile(testDbName+".wal", os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer wal.Close() + + filer, err := NewACIDFiler(NewSimpleFileFiler(f), wal) + if err != nil { + b.Error(err) + return + } + + benchmarkBTreeSetFiler(b, filer, sz) +} + +func BenchmarkBTreeSetACIDFiler0(b *testing.B) { + benchmarkBTreeSetACIDFiler(b, 0) +} + +func BenchmarkBTreeSetACIDFiler1e1(b *testing.B) { + benchmarkBTreeSetACIDFiler(b, 1e1) +} + +func BenchmarkBTreeSetACIDFiler1e2(b *testing.B) { + benchmarkBTreeSetACIDFiler(b, 1e2) +} + +func BenchmarkBTreeSetACIDFiler1e3(b *testing.B) { + benchmarkBTreeSetACIDFiler(b, 1e3) +} + +func testbTreeEnumeratorInvalidating(t *testing.T, mutate func(b *BTree) error) { + b := NewBTree(nil) + if err := b.Set([]byte{1}, []byte{2}); err != nil { + t.Fatal(err) + } + + if err := b.Set([]byte{3}, []byte{4}); err != nil { + t.Fatal(err) + } + + e, err := b.seekFirst() + if err != nil { + t.Fatal(err) + } + + if _, _, err = e.current(); err != nil { + t.Fatal(err) + } + + if err = e.next(); err != nil { + t.Fatal(err) + } + + if err = e.prev(); err != nil { + t.Fatal(err) + } + + if err = mutate(b); err != nil { + t.Fatal(err) + } + + if _, _, err = e.current(); err == nil { + t.Fatal(err) + } + + if _, ok := err.(*ErrINVAL); !ok { + t.Fatalf("%T", err) + } + + err = e.next() + if err == nil { + t.Fatal(err) + } + + if _, ok := err.(*ErrINVAL); !ok { + t.Fatalf("%T", err) + } + + err = e.prev() + if err == nil { + t.Fatal(err) + } + + if _, ok := err.(*ErrINVAL); !ok { + t.Fatalf("%T", err) + } + +} + +func TestBTreeEnumeratorInvalidating(t *testing.T) { + testbTreeEnumeratorInvalidating(t, func(b *BTree) error { return b.Clear() }) + testbTreeEnumeratorInvalidating(t, func(b *BTree) error { return b.Delete([]byte{1}) }) + testbTreeEnumeratorInvalidating(t, func(b *BTree) error { _, err := b.DeleteAny(); return err }) + testbTreeEnumeratorInvalidating(t, func(b *BTree) error { _, err := b.Extract(nil, []byte{1}); return err }) + testbTreeEnumeratorInvalidating(t, func(b *BTree) error { + _, _, err := b.Put( + nil, + []byte{1}, + func(k, o []byte) ([]byte, bool, error) { return nil, false, nil }, + ) + return err + }) + testbTreeEnumeratorInvalidating(t, func(b *BTree) error { return b.Set([]byte{4}, []byte{5}) }) +} + +func n2b(n int) []byte { + var b [8]byte + binary.BigEndian.PutUint64(b[:], uint64(n)) + return b[:] +} + +func b2n(b []byte) int { + if len(b) != 8 { + return mathutil.MinInt + } + + return int(binary.BigEndian.Uint64(b)) +} + +func TestBTreeSeekNext(t *testing.T) { + // seeking within 3 keys: 10, 20, 30 + table := []struct { + k int + hit bool + keys []int + }{ + {5, false, []int{10, 20, 30}}, + {10, true, []int{10, 20, 30}}, + {15, false, []int{20, 30}}, + {20, true, []int{20, 30}}, + {25, false, []int{30}}, + {30, true, []int{30}}, + {35, false, []int{}}, + } + + for i, test := range table { + up := test.keys + db := NewBTree(nil) + + if err := db.Set(n2b(10), n2b(100)); err != nil { + t.Fatal(i, err) + } + + if err := db.Set(n2b(20), n2b(200)); err != nil { + t.Fatal(i, err) + } + + if err := db.Set(n2b(30), n2b(300)); err != nil { + t.Fatal(i, err) + } + + for brokenSerial := 0; brokenSerial < 16; brokenSerial++ { + en, hit, err := db.Seek(n2b(test.k)) + if err != nil { + t.Fatal(err) + } + + if g, e := hit, test.hit; g != e { + t.Fatal(i, g, e) + } + + j := 0 + for { + if brokenSerial&(1<= len(up) { + t.Fatal(i, j, brokenSerial) + } + + if g, e := b2n(k), up[j]; g != e { + t.Fatal(i, j, brokenSerial, g, e) + } + + if g, e := len(v), 8; g != e { + t.Fatal(i, g, e) + } + + if g, e := b2n(v), 10*up[j]; g != e { + t.Fatal(i, g, e) + } + + j++ + + } + + if g, e := j, len(up); g != e { + t.Fatal(i, j, g, e) + } + } + + } +} + +func TestBTreeSeekPrev(t *testing.T) { + // seeking within 3 keys: 10, 20, 30 + table := []struct { + k int + hit bool + keys []int + }{ + {5, false, []int{10}}, + {10, true, []int{10}}, + {15, false, []int{20, 10}}, + {20, true, []int{20, 10}}, + {25, false, []int{30, 20, 10}}, + {30, true, []int{30, 20, 10}}, + {35, false, []int{}}, + } + + for i, test := range table { + down := test.keys + db := NewBTree(nil) + if err := db.Set(n2b(10), n2b(100)); err != nil { + t.Fatal(i, err) + } + + if err := db.Set(n2b(20), n2b(200)); err != nil { + t.Fatal(i, err) + } + + if err := db.Set(n2b(30), n2b(300)); err != nil { + t.Fatal(i, err) + } + + for brokenSerial := 0; brokenSerial < 16; brokenSerial++ { + en, hit, err := db.Seek(n2b(test.k)) + if err != nil { + t.Fatal(err) + } + + if g, e := hit, test.hit; g != e { + t.Fatal(i, g, e) + } + + j := 0 + for { + if brokenSerial&(1<= len(down) { + t.Fatal(i, j, brokenSerial) + } + + if g, e := b2n(k), down[j]; g != e { + t.Fatal(i, j, brokenSerial, g, e) + } + + if g, e := len(v), 8; g != e { + t.Fatal(i, g, e) + } + + if g, e := b2n(v), 10*down[j]; g != e { + t.Fatal(i, g, e) + } + + j++ + + } + + if g, e := j, len(down); g != e { + t.Fatal(i, j, g, e) + } + } + + } +} + +func TestBTreeSeekFirst(t *testing.T) { + db := NewBTree(nil) + en, err := db.SeekFirst() + if err == nil { + t.Fatal(err) + } + + if err := db.Set(n2b(100), n2b(1000)); err != nil { + t.Fatal(err) + } + + if en, err = db.SeekFirst(); err != nil { + t.Fatal(err) + } + + k, v, err := en.Next() + if err != nil { + t.Fatal(err) + } + + if g, e := b2n(k), 100; g != e { + t.Fatal(g, e) + } + + if g, e := b2n(v), 1000; g != e { + t.Fatal(g, e) + } + + if err := db.Set(n2b(110), n2b(1100)); err != nil { + t.Fatal(err) + } + + if en, err = db.SeekFirst(); err != nil { + t.Fatal(err) + } + + if k, v, err = en.Next(); err != nil { + t.Fatal(err) + } + + if g, e := b2n(k), 100; g != e { + t.Fatal(g, e) + } + + if g, e := b2n(v), 1000; g != e { + t.Fatal(g, e) + } + + if err := db.Set(n2b(90), n2b(900)); err != nil { + t.Fatal(err) + } + + if en, err = db.SeekFirst(); err != nil { + t.Fatal(err) + } + + if k, v, err = en.Next(); err != nil { + t.Fatal(err) + } + + if g, e := b2n(k), 90; g != e { + t.Fatal(g, e) + } + + if g, e := b2n(v), 900; g != e { + t.Fatal(g, e) + } + +} + +func TestBTreeSeekLast(t *testing.T) { + db := NewBTree(nil) + en, err := db.SeekLast() + if err == nil { + t.Fatal(err) + } + + if err := db.Set(n2b(100), n2b(1000)); err != nil { + t.Fatal(err) + } + + if en, err = db.SeekLast(); err != nil { + t.Fatal(err) + } + + k, v, err := en.Next() + if err != nil { + t.Fatal(err) + } + + if g, e := b2n(k), 100; g != e { + t.Fatal(g, e) + } + + if g, e := b2n(v), 1000; g != e { + t.Fatal(g, e) + } + + if err := db.Set(n2b(90), n2b(900)); err != nil { + t.Fatal(err) + } + + if en, err = db.SeekLast(); err != nil { + t.Fatal(err) + } + + if k, v, err = en.Next(); err != nil { + t.Fatal(err) + } + + if g, e := b2n(k), 100; g != e { + t.Fatal(g, e) + } + + if g, e := b2n(v), 1000; g != e { + t.Fatal(g, e) + } + + if err := db.Set(n2b(110), n2b(1100)); err != nil { + t.Fatal(err) + } + + if en, err = db.SeekLast(); err != nil { + t.Fatal(err) + } + + if k, v, err = en.Next(); err != nil { + t.Fatal(err) + } + + if g, e := b2n(k), 110; g != e { + t.Fatal(g, e) + } + + if g, e := b2n(v), 1100; g != e { + t.Fatal(g, e) + } + +} + +// https://code.google.com/p/camlistore/issues/detail?id=216 +func TestBug216(t *testing.T) { + const S = 2*kKV + 2 // 2*kKV+1 ok + const N = 300000 + rng, err := mathutil.NewFC32(math.MinInt32, math.MaxInt32, true) + if err != nil { + t.Fatal(err) + } + k := make([]byte, S/2) + v := make([]byte, S-S/2) + tr := NewBTree(nil) + for i := 0; i < N; i++ { + for i := range k { + k[i] = byte(rng.Next()) + } + for i := range v { + v[i] = byte(rng.Next()) + } + + if err := tr.Set(h2b(k, int64(i)), h2b(v, int64(i))); err != nil { + t.Fatal(i, err) + } + + if (i+1)%10000 == 0 { + //dbg("%v", i+1) + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/errors.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/errors.go new file mode 100644 index 000000000..7dffe7f10 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/errors.go @@ -0,0 +1,170 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Some errors returned by this package. +// +// Note that this package can return more errors than declared here, for +// example io.EOF from Filer.ReadAt(). + +package lldb + +import ( + "fmt" +) + +// ErrDecodeScalars is possibly returned from DecodeScalars +type ErrDecodeScalars struct { + B []byte // Data being decoded + I int // offending offset +} + +// Error implements the built in error type. +func (e *ErrDecodeScalars) Error() string { + return fmt.Sprintf("DecodeScalars: corrupted data @ %d/%d", e.I, len(e.B)) +} + +// ErrINVAL reports invalid values passed as parameters, for example negative +// offsets where only non-negative ones are allowed or read from the DB. +type ErrINVAL struct { + Src string + Val interface{} +} + +// Error implements the built in error type. +func (e *ErrINVAL) Error() string { + return fmt.Sprintf("%s: %+v", e.Src, e.Val) +} + +// ErrPERM is for example reported when a Filer is closed while BeginUpdate(s) +// are not balanced with EndUpdate(s)/Rollback(s) or when EndUpdate or Rollback +// is invoked which is not paired with a BeginUpdate. +type ErrPERM struct { + Src string +} + +// Error implements the built in error type. +func (e *ErrPERM) Error() string { + return fmt.Sprintf("%s: Operation not permitted", string(e.Src)) +} + +// ErrTag represents an ErrILSEQ kind. +type ErrType int + +// ErrILSEQ types +const ( + ErrOther ErrType = iota + + ErrAdjacentFree // Adjacent free blocks (.Off and .Arg) + ErrDecompress // Used compressed block: corrupted compression + ErrExpFreeTag // Expected a free block tag, got .Arg + ErrExpUsedTag // Expected a used block tag, got .Arg + ErrFLT // Free block is invalid or referenced multiple times + ErrFLTLoad // FLT truncated to .Off, need size >= .Arg + ErrFLTSize // Free block size (.Arg) doesn't belong to its list min size: .Arg2 + ErrFileSize // File .Name size (.Arg) != 0 (mod 16) + ErrFreeChaining // Free block, .prev.next doesn't point back to this block + ErrFreeTailBlock // Last block is free + ErrHead // Head of a free block list has non zero Prev (.Arg) + ErrInvalidRelocTarget // Reloc doesn't target (.Arg) a short or long used block + ErrInvalidWAL // Corrupted write ahead log. .Name: file name, .More: more + ErrLongFreeBlkTooLong // Long free block spans beyond EOF, size .Arg + ErrLongFreeBlkTooShort // Long free block must have at least 2 atoms, got only .Arg + ErrLongFreeNextBeyondEOF // Long free block .Next (.Arg) spans beyond EOF + ErrLongFreePrevBeyondEOF // Long free block .Prev (.Arg) spans beyond EOF + ErrLongFreeTailTag // Expected a long free block tail tag, got .Arg + ErrLostFreeBlock // Free block is not in any FLT list + ErrNullReloc // Used reloc block with nil target + ErrRelocBeyondEOF // Used reloc points (.Arg) beyond EOF + ErrShortFreeTailTag // Expected a short free block tail tag, got .Arg + ErrSmall // Request for a free block (.Arg) returned a too small one (.Arg2) at .Off + ErrTailTag // Block at .Off has invalid tail CC (compression code) tag, got .Arg + ErrUnexpReloc // Unexpected reloc block referred to from reloc block .Arg + ErrVerifyPadding // Used block has nonzero padding + ErrVerifyTailSize // Long free block size .Arg but tail size .Arg2 + ErrVerifyUsedSpan // Used block size (.Arg) spans beyond EOF +) + +// ErrILSEQ reports a corrupted file format. Details in fields according to Type. +type ErrILSEQ struct { + Type ErrType + Off int64 + Arg int64 + Arg2 int64 + Arg3 int64 + Name string + More interface{} +} + +// Error implements the built in error type. +func (e *ErrILSEQ) Error() string { + switch e.Type { + case ErrAdjacentFree: + return fmt.Sprintf("Adjacent free blocks at offset %#x and %#x", e.Off, e.Arg) + case ErrDecompress: + return fmt.Sprintf("Compressed block at offset %#x: Corrupted compressed content", e.Off) + case ErrExpFreeTag: + return fmt.Sprintf("Block at offset %#x: Expected a free block tag, got %#2x", e.Off, e.Arg) + case ErrExpUsedTag: + return fmt.Sprintf("Block at ofset %#x: Expected a used block tag, got %#2x", e.Off, e.Arg) + case ErrFLT: + return fmt.Sprintf("Free block at offset %#x is invalid or referenced multiple times", e.Off) + case ErrFLTLoad: + return fmt.Sprintf("FLT truncated to size %d, expected at least %d", e.Off, e.Arg) + case ErrFLTSize: + return fmt.Sprintf("Free block at offset %#x has size (%#x) should be at least (%#x)", e.Off, e.Arg, e.Arg2) + case ErrFileSize: + return fmt.Sprintf("File %q size (%#x) != 0 (mod 16)", e.Name, e.Arg) + case ErrFreeChaining: + return fmt.Sprintf("Free block at offset %#x: .prev.next doesn point back here.", e.Off) + case ErrFreeTailBlock: + return fmt.Sprintf("Free block at offset %#x: Cannot be last file block", e.Off) + case ErrHead: + return fmt.Sprintf("Block at offset %#x: Head of free block list has non zero .prev %#x", e.Off, e.Arg) + case ErrInvalidRelocTarget: + return fmt.Sprintf("Used reloc block at offset %#x: Target (%#x) is not a short or long used block", e.Off, e.Arg) + case ErrInvalidWAL: + return fmt.Sprintf("Corrupted write ahead log file: %q %v", e.Name, e.More) + case ErrLongFreeBlkTooLong: + return fmt.Sprintf("Long free block at offset %#x: Size (%#x) beyond EOF", e.Off, e.Arg) + case ErrLongFreeBlkTooShort: + return fmt.Sprintf("Long free block at offset %#x: Size (%#x) too small", e.Off, e.Arg) + case ErrLongFreeNextBeyondEOF: + return fmt.Sprintf("Long free block at offset %#x: Next (%#x) points beyond EOF", e.Off, e.Arg) + case ErrLongFreePrevBeyondEOF: + return fmt.Sprintf("Long free block at offset %#x: Prev (%#x) points beyond EOF", e.Off, e.Arg) + case ErrLongFreeTailTag: + return fmt.Sprintf("Block at offset %#x: Expected long free tail tag, got %#2x", e.Off, e.Arg) + case ErrLostFreeBlock: + return fmt.Sprintf("Free block at offset %#x: not in any FLT list", e.Off) + case ErrNullReloc: + return fmt.Sprintf("Used reloc block at offset %#x: Nil target", e.Off) + case ErrRelocBeyondEOF: + return fmt.Sprintf("Used reloc block at offset %#x: Link (%#x) points beyond EOF", e.Off, e.Arg) + case ErrShortFreeTailTag: + return fmt.Sprintf("Block at offset %#x: Expected short free tail tag, got %#2x", e.Off, e.Arg) + case ErrSmall: + return fmt.Sprintf("Request for of free block of size %d returned a too small (%d) one at offset %#x", e.Arg, e.Arg2, e.Off) + case ErrTailTag: + return fmt.Sprintf("Block at offset %#x: Invalid tail CC tag, got %#2x", e.Off, e.Arg) + case ErrUnexpReloc: + return fmt.Sprintf("Block at offset %#x: Unexpected reloc block. Referred to from reloc block at offset %#x", e.Off, e.Arg) + case ErrVerifyPadding: + return fmt.Sprintf("Used block at offset %#x: Nonzero padding", e.Off) + case ErrVerifyTailSize: + return fmt.Sprintf("Long free block at offset %#x: Size %#x, but tail size %#x", e.Off, e.Arg, e.Arg2) + case ErrVerifyUsedSpan: + return fmt.Sprintf("Used block at offset %#x: Size %#x spans beyond EOF", e.Off, e.Arg) + } + + more := "" + if e.More != nil { + more = fmt.Sprintf(", %v", e.More) + } + off := "" + if e.Off != 0 { + off = fmt.Sprintf(", off: %#x", e.Off) + } + + return fmt.Sprintf("Error%s%s", off, more) +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/falloc.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/falloc.go new file mode 100644 index 000000000..2969036e2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/falloc.go @@ -0,0 +1,1981 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The storage space management. + +package lldb + +import ( + "bytes" + "errors" + "fmt" + "io" + "sort" + "strings" + "sync" + + "github.com/cznic/bufs" + "github.com/cznic/mathutil" + "github.com/cznic/zappy" +) + +const ( + maxBuf = maxRq + 20 // bufs,Buffers.Alloc +) + +// Options are passed to the NewAllocator to amend some configuration. The +// compatibility promise is the same as of struct types in the Go standard +// library - introducing changes can be made only by adding new exported +// fields, which is backward compatible as long as client code uses field names +// to assign values of imported struct types literals. +// +// NOTE: No options are currently defined. +type Options struct{} + +// AllocStats record statistics about a Filer. It can be optionally filled by +// Allocator.Verify, if successful. +type AllocStats struct { + Handles int64 // total valid handles in use + Compression int64 // number of compressed blocks + TotalAtoms int64 // total number of atoms == AllocAtoms + FreeAtoms + AllocBytes int64 // bytes allocated (after decompression, if/where used) + AllocAtoms int64 // atoms allocated/used, including relocation atoms + Relocations int64 // number of relocated used blocks + FreeAtoms int64 // atoms unused + AllocMap map[int64]int64 // allocated block size in atoms -> count of such blocks + FreeMap map[int64]int64 // free block size in atoms -> count of such blocks +} + +/* + +Allocator implements "raw" storage space management (allocation and +deallocation) for a low level of a DB engine. The storage is an abstraction +provided by a Filer. + +The terms MUST or MUST NOT, if/where used in the documentation of Allocator, +written in all caps as seen here, are a requirement for any possible +alternative implementations aiming for compatibility with this one. + +Filer file + +A Filer file, or simply 'file', is a linear, contiguous sequence of blocks. +Blocks may be either free (currently unused) or allocated (currently used). +Some blocks may eventually become virtual in a sense as they may not be +realized in the storage (sparse files). + +Free Lists Table + +File starts with a FLT. This table records heads of 14 doubly linked free +lists. The zero based index (I) vs minimal size of free blocks in that list, +except the last one which registers free blocks of size 4112+ atoms: + + MinSize == 2^I + + For example 0 -> 1, 1 -> 2, ... 12 -> 4096. + +Each entry in the FLT is 8 bytes in netwtork order, MSB MUST be zero, ie. the +slot value is effectively only 7 bytes. The value is the handle of the head of +the respective doubly linked free list. The FLT size is 14*8 == 112(0x70) +bytes. If the free blocks list for any particular size is empty, the respective +FLT slot is zero. Sizes of free blocks in one list MUST NOT overlap with sizes +of free lists in other list. For example, even though a free block of size 2 +technically is of minimal size >= 1, it MUST NOT be put to the list for slot 0 +(minimal size 1), but in slot 1( minimal size 2). + + slot 0: sizes [1, 2) + slot 1: sizes [2, 4) + slot 2: sizes [4, 8) + ... + slot 11: sizes [2048, 4096) + slot 12: sizes [4096, 4112) + slot 13: sizes [4112, inf) + +The last FLT slot collects all free blocks bigger than its minimal size. That +still respects the 'no overlap' invariant. + +File blocks + +A block is a linear, contiguous sequence of atoms. The first and last atoms of +a block provide information about, for example, whether the block is free or +used, what is the size of the block, etc. Details are discussed elsewhere. The +first block of a file starts immediately after FLT, ie. at file offset +112(0x70). + +Block atoms + +An atom is a fixed size piece of a block (and thus of a file too); it is 16 +bytes long. A consequence is that for a valid file: + + filesize == 0 (mod 16) + +The first atom of the first block is considered to be atom #1. + +Block handles + +A handle is an integer referring to a block. The reference is the number of the +atom the block starts with. Put in other way: + + handle == offset/16 - 6 + offset == 16 * (handle + 6) + +`offset` is the offset of the first byte of the block, measured in bytes +- as in fseek(3). Handle has type `int64`, but only the lower 7 bytes may be +nonzero while referring to a block, both in code as well as when persisted in +the the file's internal bookkeeping structures - see 'Block types' bellow. So a +handle is effectively only `uint56`. This also means that the maximum usable +size of a file is 2^56 atoms. That is 2^60 bytes == 1 exabyte (10^18 bytes). + +Nil handles + +A handle with numeric value of '0' refers to no block. + +Zero padding + +A padding is used to round-up a block size to be a whole number of atoms. Any +padding, if present, MUST be all zero bytes. Note that the size of padding is +in [0, 15]. + +Content wiping + +When a block is deallocated, its data content is not wiped as the added +overhead may be substantial while not necessarily needed. Client code should +however overwrite the content of any block having sensitive data with eg. zeros +(good compression) - before deallocating the block. + +Block tags + +Every block is tagged in its first byte (a head tag) and last byte (tail tag). +Block types are: + + 1. Short content used block (head tags 0x00-0xFB) + 2. Long content used block (head tag 0xFC) + 3. Relocated used block (head tag 0xFD) + 4. Short, single atom, free block (head tag 0xFE) + 5. Long free block (head tag 0xFF) + +Note: Relocated used block, 3. above (head tag 0xFD) MUST NOT refer to blocks +other then 1. or 2. above (head tags 0x00-0xFC). + +Content blocks + +Used blocks (head tags 0x00-0xFC) tail tag distinguish used/unused block and if +content is compressed or not. + +Content compression + +The tail flag of an used block is one of + + CC == 0 // Content is not compressed. + CC == 1 // Content is in zappy compression format. + +If compression of written content is enabled, there are two cases: If +compressed size < original size then the compressed content should be written +if it will save at least one atom of the block. If compressed size >= original +size then the compressed content should not be used. + +It's recommended to use compression. For example the BTrees implementation +assumes compression is used. Using compression may cause a slowdown in some +cases while it may as well cause a speedup. + +Short content block + +Short content block carries content of length between N == 0(0x00) and N == +251(0xFB) bytes. + + |<-first atom start ... last atom end->| + +---++-- ... --+-- ... --++------+ + | 0 || 1... | 0x*...0x*E || 0x*F | + +---++-- ... --+-- ... --++------+ + | N || content | padding || CC | + +---++-- ... --+-- ... --++------+ + + A == (N+1)/16 + 1 // The number of atoms in the block [1, 16] + padding == 15 - (N+1)%16 // Length of the zero padding + +Long content block + +Long content block carries content of length between N == 252(0xFC) and N == +65787(0x100FB) bytes. + + |<-first atom start ... last atom end->| + +------++------+-- ... --+-- ... --++------+ + | 0 || 1..2 | 3... | 0x*...0x*E || 0x*F | + +------++------+-- ... --+-- ... --++------+ + | 0xFC || M | content | padding || CC | + +------++------+-- ... --+-- ... --++------+ + + A == (N+3)/16 + 1 // The number of atoms in the block [16, 4112] + M == N % 0x10000 // Stored as 2 bytes in network byte order + padding == 15 - (N+3)%16 // Length of the zero padding + +Relocated used block + +Relocated block allows to permanently assign a handle to some content and +resize the content anytime afterwards without having to update all the possible +existing references; the handle can be constant while the content size may be +dynamic. When relocating a block, any space left by the original block content, +above this single atom block, MUST be reclaimed. + +Relocations MUST point only to a used short or long block == blocks with tags +0x00...0xFC. + + +------++------+---------++----+ + | 0 || 1..7 | 8...14 || 15 | + +------++------+---------++----+ + | 0xFD || H | padding || 0 | + +------++------+---------++----+ + +H is the handle of the relocated block in network byte order. + +Free blocks + +Free blocks are the result of space deallocation. Free blocks are organized in +one or more doubly linked lists, abstracted by the FLT interface. Free blocks +MUST be "registered" by putting them in such list. Allocator MUST reuse a big +enough free block, if such exists, before growing the file size. When a free +block is created by deallocation or reallocation it MUST be joined with any +adjacently existing free blocks before "registering". If the resulting free +block is now a last block of a file, the free block MUST be discarded and the +file size MUST be truncated accordingly instead. Put differently, there MUST +NOT ever be a free block at the file end. + +A single free atom + +Is an unused block of size 1 atom. + + +------++------+--------++------+ + | 0 || 1..7 | 8...14 || 15 | + +------++------+--------++------+ + | 0xFE || P | N || 0xFE | + +------++------+--------++------+ + +P and N, stored in network byte order, are the previous and next free block +handles in the doubly linked list to which this free block belongs. + +A long unused block + +Is an unused block of size > 1 atom. + + +------++------+-------+---------+- ... -+----------++------+ + | 0 || 1..7 | 8..14 | 15...21 | | Z-7..Z-1 || Z | + +------++------+-------+---------+- ... -+----------++------+ + | 0xFF || S | P | N | Leak | S || 0xFF | + +------++------+-------+---------+- ... -+----------++------+ + + Z == 16 * S - 1 + +S is the size of this unused block in atoms. P and N are the previous and next +free block handles in the doubly linked list to which this free block belongs. +Leak contains any data the block had before deallocating this block. See also +the subtitle 'Content wiping' above. S, P and N are stored in network byte +order. Large free blocks may trigger a consideration of file hole punching of +the Leak field - for some value of 'large'. + +Note: Allocator methods vs CRUD[1]: + + Alloc [C]reate + Get [R]ead + Realloc [U]pdate + Free [D]elete + +Note: No Allocator method returns io.EOF. + + [1]: http://en.wikipedia.org/wiki/Create,_read,_update_and_delete + +*/ +type Allocator struct { + f Filer + flt flt + Compress bool // enables content compression + cache cache + m map[int64]*node + lru lst + expHit int64 + expMiss int64 + cacheSz int + hit uint16 + miss uint16 + mu sync.Mutex +} + +// NewAllocator returns a new Allocator. To open an existing file, pass its +// Filer. To create a "new" file, pass a Filer which file is of zero size. +func NewAllocator(f Filer, opts *Options) (a *Allocator, err error) { + if opts == nil { // Enforce *Options is always passed + return nil, errors.New("NewAllocator: nil opts passed") + } + + a = &Allocator{ + f: f, + cacheSz: 10, + } + + a.cinit() + switch x := f.(type) { + case *RollbackFiler: + x.afterRollback = func() error { + a.cinit() + return a.flt.load(a.f, 0) + } + case *ACIDFiler0: + x.RollbackFiler.afterRollback = func() error { + a.cinit() + return a.flt.load(a.f, 0) + } + } + + sz, err := f.Size() + if err != nil { + return + } + + a.flt.init() + if sz == 0 { + var b [fltSz]byte + if err = a.f.BeginUpdate(); err != nil { + return + } + + if _, err = f.WriteAt(b[:], 0); err != nil { + a.f.Rollback() + return + } + + return a, a.f.EndUpdate() + } + + return a, a.flt.load(f, 0) +} + +// CacheStats reports cache statistics. +// +//TODO return a struct perhaps. +func (a *Allocator) CacheStats() (buffersUsed, buffersTotal int, bytesUsed, bytesTotal, hits, misses int64) { + buffersUsed = len(a.m) + buffersTotal = buffersUsed + len(a.cache) + bytesUsed = a.lru.size() + bytesTotal = bytesUsed + a.cache.size() + hits = a.expHit + misses = a.expMiss + return +} + +func (a *Allocator) cinit() { + for h, n := range a.m { + a.cache.put(a.lru.remove(n)) + delete(a.m, h) + } + if a.m == nil { + a.m = map[int64]*node{} + } +} + +func (a *Allocator) cadd(b []byte, h int64) { + if len(a.m) < a.cacheSz { + n := a.cache.get(len(b)) + n.h = h + copy(n.b, b) + a.m[h] = a.lru.pushFront(n) + return + } + + // cache full + delete(a.m, a.cache.put(a.lru.removeBack()).h) + n := a.cache.get(len(b)) + n.h = h + copy(n.b, b) + a.m[h] = a.lru.pushFront(n) + return +} + +func (a *Allocator) cfree(h int64) { + n, ok := a.m[h] + if !ok { // must have been evicted + return + } + + a.cache.put(a.lru.remove(n)) + delete(a.m, h) +} + +// Alloc allocates storage space for b and returns the handle of the new block +// with content set to b or an error, if any. The returned handle is valid only +// while the block is used - until the block is deallocated. No two valid +// handles share the same value within the same Filer, but any value of a +// handle not referring to any used block may become valid any time as a result +// of Alloc. +// +// Invoking Alloc on an empty Allocator is guaranteed to return handle with +// value 1. The intended use of content of handle 1 is a root "directory" of +// other data held by an Allocator. +// +// Passing handles not obtained initially from Alloc or not anymore valid to +// any other Allocator methods can result in an irreparably corrupted database. +func (a *Allocator) Alloc(b []byte) (handle int64, err error) { + buf := bufs.GCache.Get(zappy.MaxEncodedLen(len(b))) + defer bufs.GCache.Put(buf) + buf, _, cc, err := a.makeUsedBlock(buf, b) + if err != nil { + return + } + + if handle, err = a.alloc(buf, cc); err == nil { + a.cadd(b, handle) + } + return +} + +func (a *Allocator) alloc(b []byte, cc byte) (h int64, err error) { + rqAtoms := n2atoms(len(b)) + if h = a.flt.find(rqAtoms); h == 0 { // must grow + var sz int64 + if sz, err = a.f.Size(); err != nil { + return + } + + h = off2h(sz) + err = a.writeUsedBlock(h, cc, b) + return + } + + // Handle is the first item of a free blocks list. + tag, s, prev, next, err := a.nfo(h) + if err != nil { + return + } + + if tag != tagFreeShort && tag != tagFreeLong { + err = &ErrILSEQ{Type: ErrExpFreeTag, Off: h2off(h), Arg: int64(tag)} + return + } + + if prev != 0 { + err = &ErrILSEQ{Type: ErrHead, Off: h2off(h), Arg: prev} + return + } + + if s < int64(rqAtoms) { + err = &ErrILSEQ{Type: ErrSmall, Arg: int64(rqAtoms), Arg2: s, Off: h2off(h)} + return + } + + if err = a.unlink(h, s, prev, next); err != nil { + return + } + + if s > int64(rqAtoms) { + freeH := h + int64(rqAtoms) + freeAtoms := s - int64(rqAtoms) + if err = a.link(freeH, freeAtoms); err != nil { + return + } + } + return h, a.writeUsedBlock(h, cc, b) +} + +// Free deallocates the block referred to by handle or returns an error, if +// any. +// +// After Free succeeds, handle is invalid and must not be used. +// +// Handle must have been obtained initially from Alloc and must be still valid, +// otherwise a database may get irreparably corrupted. +func (a *Allocator) Free(handle int64) (err error) { + if handle <= 0 || handle > maxHandle { + return &ErrINVAL{"Allocator.Free: handle out of limits", handle} + } + + a.cfree(handle) + return a.free(handle, 0, true) +} + +func (a *Allocator) free(h, from int64, acceptRelocs bool) (err error) { + tag, atoms, _, n, err := a.nfo(h) + if err != nil { + return + } + + switch tag { + default: + // nop + case tagUsedLong: + // nop + case tagUsedRelocated: + if !acceptRelocs { + return &ErrILSEQ{Type: ErrUnexpReloc, Off: h2off(h), Arg: h2off(from)} + } + + if err = a.free(n, h, false); err != nil { + return + } + case tagFreeShort, tagFreeLong: + return &ErrINVAL{"Allocator.Free: attempt to free a free block at off", h2off(h)} + } + + return a.free2(h, atoms) +} + +func (a *Allocator) free2(h, atoms int64) (err error) { + sz, err := a.f.Size() + if err != nil { + return + } + + ltag, latoms, lp, ln, err := a.leftNfo(h) + if err != nil { + return + } + + if ltag != tagFreeShort && ltag != tagFreeLong { + latoms = 0 + } + + var rtag byte + var ratoms, rp, rn int64 + + isTail := h2off(h)+atoms*16 == sz + if !isTail { + if rtag, ratoms, rp, rn, err = a.nfo(h + atoms); err != nil { + return + } + } + + if rtag != tagFreeShort && rtag != tagFreeLong { + ratoms = 0 + } + + switch { + case latoms == 0 && ratoms == 0: + // -> isolated <- + if isTail { // cut tail + return a.f.Truncate(h2off(h)) + } + + return a.link(h, atoms) + case latoms == 0 && ratoms != 0: + // right join -> + if err = a.unlink(h+atoms, ratoms, rp, rn); err != nil { + return + } + + return a.link(h, atoms+ratoms) + case latoms != 0 && ratoms == 0: + // <- left join + if err = a.unlink(h-latoms, latoms, lp, ln); err != nil { + return + } + + if isTail { + return a.f.Truncate(h2off(h - latoms)) + } + + return a.link(h-latoms, latoms+atoms) + } + + // case latoms != 0 && ratoms != 0: + // <- middle join -> + lh, rh := h-latoms, h+atoms + if err = a.unlink(lh, latoms, lp, ln); err != nil { + return + } + + // Prev unlink may have invalidated rp or rn + if _, _, rp, rn, err = a.nfo(rh); err != nil { + return + } + + if err = a.unlink(rh, ratoms, rp, rn); err != nil { + return + } + + return a.link(h-latoms, latoms+atoms+ratoms) +} + +// Add a free block h to the appropriate free list +func (a *Allocator) link(h, atoms int64) (err error) { + if err = a.makeFree(h, atoms, 0, a.flt.head(atoms)); err != nil { + return + } + + return a.flt.setHead(h, atoms, a.f) +} + +// Remove free block h from the free list +func (a *Allocator) unlink(h, atoms, p, n int64) (err error) { + switch { + case p == 0 && n == 0: + // single item list, must be head + return a.flt.setHead(0, atoms, a.f) + case p == 0 && n != 0: + // head of list (has next item[s]) + if err = a.prev(n, 0); err != nil { + return + } + + // new head + return a.flt.setHead(n, atoms, a.f) + case p != 0 && n == 0: + // last item in list + return a.next(p, 0) + } + // case p != 0 && n != 0: + // intermediate item in a list + if err = a.next(p, n); err != nil { + return + } + + return a.prev(n, p) +} + +//TODO remove ? +// Return len(slice) == n, reuse src if possible. +func need(n int, src []byte) []byte { + if cap(src) < n { + bufs.GCache.Put(src) + return bufs.GCache.Get(n) + } + + return src[:n] +} + +// Get returns the data content of a block referred to by handle or an error if +// any. The returned slice may be a sub-slice of buf if buf was large enough +// to hold the entire content. Otherwise, a newly allocated slice will be +// returned. It is valid to pass a nil buf. +// +// If the content was stored using compression then it is transparently +// returned decompressed. +// +// Handle must have been obtained initially from Alloc and must be still valid, +// otherwise invalid data may be returned without detecting the error. +// +// Get is safe for concurrent access by multiple goroutines iff no other +// goroutine mutates the DB. +func (a *Allocator) Get(buf []byte, handle int64) (b []byte, err error) { + buf = buf[:cap(buf)] + a.mu.Lock() // X1+ + if n, ok := a.m[handle]; ok { + a.lru.moveToFront(n) + b = need(len(n.b), buf) + copy(b, n.b) + a.expHit++ + a.hit++ + a.mu.Unlock() // X1- + return + } + + a.expMiss++ + a.miss++ + if a.miss > 10 && len(a.m) < 500 { + if 100*a.hit/a.miss < 95 { + a.cacheSz++ + } + a.hit, a.miss = 0, 0 + } + a.mu.Unlock() // X1- + + defer func(h int64) { + if err == nil { + a.mu.Lock() // X2+ + a.cadd(b, h) + a.mu.Unlock() // X2- + } + }(handle) + + first := bufs.GCache.Get(16) + defer bufs.GCache.Put(first) + relocated := false + relocSrc := handle +reloc: + if handle <= 0 || handle > maxHandle { + return nil, &ErrINVAL{"Allocator.Get: handle out of limits", handle} + } + + off := h2off(handle) + if err = a.read(first, off); err != nil { + return + } + + switch tag := first[0]; tag { + default: + dlen := int(tag) + atoms := n2atoms(dlen) + switch atoms { + case 1: + switch tag := first[15]; tag { + default: + return nil, &ErrILSEQ{Type: ErrTailTag, Off: off, Arg: int64(tag)} + case tagNotCompressed: + b = need(dlen, buf) + copy(b, first[1:]) + return + case tagCompressed: + return zappy.Decode(buf, first[1:dlen+1]) + } + default: + cc := bufs.GCache.Get(1) + defer bufs.GCache.Put(cc) + dlen := int(tag) + atoms := n2atoms(dlen) + tailOff := off + 16*int64(atoms) - 1 + if err = a.read(cc, tailOff); err != nil { + return + } + + switch tag := cc[0]; tag { + default: + return nil, &ErrILSEQ{Type: ErrTailTag, Off: off, Arg: int64(tag)} + case tagNotCompressed: + b = need(dlen, buf) + off += 1 + if err = a.read(b, off); err != nil { + b = buf[:0] + } + return + case tagCompressed: + zbuf := bufs.GCache.Get(dlen) + defer bufs.GCache.Put(zbuf) + off += 1 + if err = a.read(zbuf, off); err != nil { + return buf[:0], err + } + + return zappy.Decode(buf, zbuf) + } + } + case 0: + return buf[:0], nil + case tagUsedLong: + cc := bufs.GCache.Get(1) + defer bufs.GCache.Put(cc) + dlen := m2n(int(first[1])<<8 | int(first[2])) + atoms := n2atoms(dlen) + tailOff := off + 16*int64(atoms) - 1 + if err = a.read(cc, tailOff); err != nil { + return + } + + switch tag := cc[0]; tag { + default: + return nil, &ErrILSEQ{Type: ErrTailTag, Off: off, Arg: int64(tag)} + case tagNotCompressed: + b = need(dlen, buf) + off += 3 + if err = a.read(b, off); err != nil { + b = buf[:0] + } + return + case tagCompressed: + zbuf := bufs.GCache.Get(dlen) + defer bufs.GCache.Put(zbuf) + off += 3 + if err = a.read(zbuf, off); err != nil { + return buf[:0], err + } + + return zappy.Decode(buf, zbuf) + } + case tagFreeShort, tagFreeLong: + return nil, &ErrILSEQ{Type: ErrExpUsedTag, Off: off, Arg: int64(tag)} + case tagUsedRelocated: + if relocated { + return nil, &ErrILSEQ{Type: ErrUnexpReloc, Off: off, Arg: relocSrc} + } + + handle = b2h(first[1:]) + relocated = true + goto reloc + } +} + +var reallocTestHook bool + +// Realloc sets the content of a block referred to by handle or returns an +// error, if any. +// +// Handle must have been obtained initially from Alloc and must be still valid, +// otherwise a database may get irreparably corrupted. +func (a *Allocator) Realloc(handle int64, b []byte) (err error) { + if handle <= 0 || handle > maxHandle { + return &ErrINVAL{"Realloc: handle out of limits", handle} + } + + a.cfree(handle) + if err = a.realloc(handle, b); err != nil { + return + } + + if reallocTestHook { + if err = cacheAudit(a.m, &a.lru); err != nil { + return + } + } + + a.cadd(b, handle) + return +} + +func (a *Allocator) realloc(handle int64, b []byte) (err error) { + var dlen, needAtoms0 int + + b8 := bufs.GCache.Get(8) + defer bufs.GCache.Put(b8) + dst := bufs.GCache.Get(zappy.MaxEncodedLen(len(b))) + defer bufs.GCache.Put(dst) + b, needAtoms0, cc, err := a.makeUsedBlock(dst, b) + if err != nil { + return + } + + needAtoms := int64(needAtoms0) + off := h2off(handle) + if err = a.read(b8[:], off); err != nil { + return + } + + switch tag := b8[0]; tag { + default: + dlen = int(b8[0]) + case tagUsedLong: + dlen = m2n(int(b8[1])<<8 | int(b8[2])) + case tagUsedRelocated: + if err = a.free(b2h(b8[1:]), handle, false); err != nil { + return err + } + + dlen = 0 + case tagFreeShort, tagFreeLong: + return &ErrINVAL{"Allocator.Realloc: invalid handle", handle} + } + + atoms := int64(n2atoms(dlen)) +retry: + switch { + case needAtoms < atoms: + // in place shrink + if err = a.writeUsedBlock(handle, cc, b); err != nil { + return + } + + fh, fa := handle+needAtoms, atoms-needAtoms + sz, err := a.f.Size() + if err != nil { + return err + } + + if h2off(fh)+16*fa == sz { + return a.f.Truncate(h2off(fh)) + } + + return a.free2(fh, fa) + case needAtoms == atoms: + // in place replace + return a.writeUsedBlock(handle, cc, b) + } + + // case needAtoms > atoms: + // in place extend or relocate + var sz int64 + if sz, err = a.f.Size(); err != nil { + return + } + + off = h2off(handle) + switch { + case off+atoms*16 == sz: + // relocating tail block - shortcut + return a.writeUsedBlock(handle, cc, b) + default: + if off+atoms*16 < sz { + // handle is not a tail block, check right neighbour + rh := handle + atoms + rtag, ratoms, p, n, e := a.nfo(rh) + if e != nil { + return e + } + + if rtag == tagFreeShort || rtag == tagFreeLong { + // Right neighbour is a free block + if needAtoms <= atoms+ratoms { + // can expand in place + if err = a.unlink(rh, ratoms, p, n); err != nil { + return + } + + atoms += ratoms + goto retry + + } + } + } + } + + if atoms > 1 { + if err = a.realloc(handle, nil); err != nil { + return + } + } + + var newH int64 + if newH, err = a.alloc(b, cc); err != nil { + return err + } + + rb := bufs.GCache.Cget(16) + defer bufs.GCache.Put(rb) + rb[0] = tagUsedRelocated + h2b(rb[1:], newH) + if err = a.writeAt(rb[:], h2off(handle)); err != nil { + return + } + + return a.writeUsedBlock(newH, cc, b) +} + +func (a *Allocator) writeAt(b []byte, off int64) (err error) { + var n int + if n, err = a.f.WriteAt(b, off); err != nil { + return + } + + if n != len(b) { + err = io.ErrShortWrite + } + return +} + +func (a *Allocator) write(off int64, b ...[]byte) (err error) { + rq := 0 + for _, part := range b { + rq += len(part) + } + buf := bufs.GCache.Get(rq) + defer bufs.GCache.Put(buf) + buf = buf[:0] + for _, part := range b { + buf = append(buf, part...) + } + return a.writeAt(buf, off) +} + +func (a *Allocator) read(b []byte, off int64) (err error) { + var rn int + if rn, err = a.f.ReadAt(b, off); rn != len(b) { + return &ErrILSEQ{Type: ErrOther, Off: off, More: err} + } + + return nil +} + +// nfo returns h's tag. If it's a free block then return also (s)ize (in +// atoms), (p)rev and (n)ext. If it's a used block then only (s)ize is returned +// (again in atoms). If it's a used relocate block then (n)ext is set to the +// relocation target handle. +func (a *Allocator) nfo(h int64) (tag byte, s, p, n int64, err error) { + off := h2off(h) + rq := int64(22) + sz, err := a.f.Size() + if err != nil { + return + } + + if off+rq >= sz { + if rq = sz - off; rq < 15 { + err = io.ErrUnexpectedEOF + return + } + } + + buf := bufs.GCache.Get(22) + defer bufs.GCache.Put(buf) + if err = a.read(buf[:rq], off); err != nil { + return + } + + switch tag = buf[0]; tag { + default: + s = int64(n2atoms(int(tag))) + case tagUsedLong: + s = int64(n2atoms(m2n(int(buf[1])<<8 | int(buf[2])))) + case tagFreeLong: + if rq < 22 { + err = io.ErrUnexpectedEOF + return + } + + s, p, n = b2h(buf[1:]), b2h(buf[8:]), b2h(buf[15:]) + case tagUsedRelocated: + s, n = 1, b2h(buf[1:]) + case tagFreeShort: + s, p, n = 1, b2h(buf[1:]), b2h(buf[8:]) + } + return +} + +// leftNfo returns nfo for h's left neighbor if h > 1 and the left neighbor is +// a free block. Otherwise all zero values are returned instead. +func (a *Allocator) leftNfo(h int64) (tag byte, s, p, n int64, err error) { + if !(h > 1) { + return + } + + buf := bufs.GCache.Get(8) + defer bufs.GCache.Put(buf) + off := h2off(h) + if err = a.read(buf[:], off-8); err != nil { + return + } + + switch tag := buf[7]; tag { + case tagFreeShort: + return a.nfo(h - 1) + case tagFreeLong: + return a.nfo(h - b2h(buf[:])) + } + return +} + +// Set h.prev = p +func (a *Allocator) prev(h, p int64) (err error) { + b := bufs.GCache.Get(7) + defer bufs.GCache.Put(b) + off := h2off(h) + if err = a.read(b[:1], off); err != nil { + return + } + + switch tag := b[0]; tag { + default: + return &ErrILSEQ{Type: ErrExpFreeTag, Off: off, Arg: int64(tag)} + case tagFreeShort: + off += 1 + case tagFreeLong: + off += 8 + } + return a.writeAt(h2b(b[:7], p), off) +} + +// Set h.next = n +func (a *Allocator) next(h, n int64) (err error) { + b := bufs.GCache.Get(7) + defer bufs.GCache.Put(b) + off := h2off(h) + if err = a.read(b[:1], off); err != nil { + return + } + + switch tag := b[0]; tag { + default: + return &ErrILSEQ{Type: ErrExpFreeTag, Off: off, Arg: int64(tag)} + case tagFreeShort: + off += 8 + case tagFreeLong: + off += 15 + } + return a.writeAt(h2b(b[:7], n), off) +} + +// Make the filer image @h a free block. +func (a *Allocator) makeFree(h, atoms, prev, next int64) (err error) { + buf := bufs.GCache.Get(22) + defer bufs.GCache.Put(buf) + switch { + case atoms == 1: + buf[0], buf[15] = tagFreeShort, tagFreeShort + h2b(buf[1:], prev) + h2b(buf[8:], next) + if err = a.write(h2off(h), buf[:16]); err != nil { + return + } + default: + + buf[0] = tagFreeLong + h2b(buf[1:], atoms) + h2b(buf[8:], prev) + h2b(buf[15:], next) + if err = a.write(h2off(h), buf[:22]); err != nil { + return + } + + h2b(buf[:], atoms) + buf[7] = tagFreeLong + if err = a.write(h2off(h+atoms)-8, buf[:8]); err != nil { + return + } + } + if prev != 0 { + if err = a.next(prev, h); err != nil { + return + } + } + + if next != 0 { + err = a.prev(next, h) + } + return +} + +func (a *Allocator) makeUsedBlock(dst []byte, b []byte) (w []byte, rqAtoms int, cc byte, err error) { + cc = tagNotCompressed + w = b + + var n int + if n = len(b); n > maxRq { + return nil, 0, 0, &ErrINVAL{"Allocator.makeUsedBlock: content size out of limits", n} + } + + rqAtoms = n2atoms(n) + if a.Compress && n > 14 { // attempt compression + if dst, err = zappy.Encode(dst, b); err != nil { + return + } + + n2 := len(dst) + if rqAtoms2 := n2atoms(n2); rqAtoms2 < rqAtoms { // compression saved at least a single atom + w, n, rqAtoms, cc = dst, n2, rqAtoms2, tagCompressed + } + } + return +} + +func (a *Allocator) writeUsedBlock(h int64, cc byte, b []byte) (err error) { + n := len(b) + rq := n2atoms(n) << 4 + buf := bufs.GCache.Get(rq) + defer bufs.GCache.Put(buf) + switch n <= maxShort { + case true: + buf[0] = byte(n) + copy(buf[1:], b) + case false: + m := n2m(n) + buf[0], buf[1], buf[2] = tagUsedLong, byte(m>>8), byte(m) + copy(buf[3:], b) + } + if p := n2padding(n); p != 0 { + copy(buf[rq-1-p:], zeros[:]) + } + buf[rq-1] = cc + return a.writeAt(buf, h2off(h)) +} + +func (a *Allocator) verifyUnused(h, totalAtoms int64, tag byte, log func(error) bool, fast bool) (atoms, prev, next int64, err error) { + switch tag { + default: + panic("internal error") + case tagFreeShort: + var b [16]byte + off := h2off(h) + if err = a.read(b[:], off); err != nil { + return + } + + if b[15] != tagFreeShort { + err = &ErrILSEQ{Type: ErrShortFreeTailTag, Off: off, Arg: int64(b[15])} + log(err) + return + } + + atoms, prev, next = 1, b2h(b[1:]), b2h(b[8:]) + case tagFreeLong: + var b [22]byte + off := h2off(h) + if err = a.read(b[:], off); err != nil { + return + } + + atoms, prev, next = b2h(b[1:]), b2h(b[8:]), b2h(b[15:]) + if fast { + return + } + + if atoms < 2 { + err = &ErrILSEQ{Type: ErrLongFreeBlkTooShort, Off: off, Arg: int64(atoms)} + break + } + + if h+atoms-1 > totalAtoms { + err = &ErrILSEQ{Type: ErrLongFreeBlkTooLong, Off: off, Arg: atoms} + break + } + + if prev > totalAtoms { + err = &ErrILSEQ{Type: ErrLongFreePrevBeyondEOF, Off: off, Arg: next} + break + } + + if next > totalAtoms { + err = &ErrILSEQ{Type: ErrLongFreeNextBeyondEOF, Off: off, Arg: next} + break + } + + toff := h2off(h+atoms) - 8 + if err = a.read(b[:8], toff); err != nil { + return + } + + if b[7] != tag { + err = &ErrILSEQ{Type: ErrLongFreeTailTag, Off: off, Arg: int64(b[7])} + break + } + + if s2 := b2h(b[:]); s2 != atoms { + err = &ErrILSEQ{Type: ErrVerifyTailSize, Off: off, Arg: atoms, Arg2: s2} + break + } + + } + if err != nil { + log(err) + } + return +} + +func (a *Allocator) verifyUsed(h, totalAtoms int64, tag byte, buf, ubuf []byte, log func(error) bool, fast bool) (compressed bool, dlen int, atoms, link int64, err error) { + var ( + padding int + doff int64 + padZeros [15]byte + tailBuf [16]byte + ) + + switch tag { + default: // Short used + dlen = int(tag) + atoms = int64((dlen+1)/16) + 1 + padding = 15 - (dlen+1)%16 + doff = h2off(h) + 1 + case tagUsedLong: + off := h2off(h) + 1 + var b2 [2]byte + if err = a.read(b2[:], off); err != nil { + return + } + + dlen = m2n(int(b2[0])<<8 | int(b2[1])) + atoms = int64((dlen+3)/16) + 1 + padding = 15 - (dlen+3)%16 + doff = h2off(h) + 3 + case tagUsedRelocated: + dlen = 7 + atoms = 1 + padding = 7 + doff = h2off(h) + 1 + case tagFreeShort, tagFreeLong: + panic("internal error") + } + + if fast { + if tag == tagUsedRelocated { + dlen = 0 + if err = a.read(buf[:7], doff); err != nil { + return + } + + link = b2h(buf) + } + + return false, dlen, atoms, link, nil + } + + if ok := h+atoms-1 <= totalAtoms; !ok { // invalid last block + err = &ErrILSEQ{Type: ErrVerifyUsedSpan, Off: h2off(h), Arg: atoms} + log(err) + return + } + + tailsz := 1 + padding + off := h2off(h) + 16*atoms - int64(tailsz) + if err = a.read(tailBuf[:tailsz], off); err != nil { + return false, 0, 0, 0, err + } + + if ok := bytes.Equal(padZeros[:padding], tailBuf[:padding]); !ok { + err = &ErrILSEQ{Type: ErrVerifyPadding, Off: h2off(h)} + log(err) + return + } + + var cc byte + switch cc = tailBuf[padding]; cc { + default: + err = &ErrILSEQ{Type: ErrTailTag, Off: h2off(h)} + log(err) + return + case tagCompressed: + compressed = true + if tag == tagUsedRelocated { + err = &ErrILSEQ{Type: ErrTailTag, Off: h2off(h)} + log(err) + return + } + + fallthrough + case tagNotCompressed: + if err = a.read(buf[:dlen], doff); err != nil { + return false, 0, 0, 0, err + } + } + + if cc == tagCompressed { + if ubuf, err = zappy.Decode(ubuf, buf[:dlen]); err != nil || len(ubuf) > maxRq { + err = &ErrILSEQ{Type: ErrDecompress, Off: h2off(h)} + log(err) + return + } + + dlen = len(ubuf) + } + + if tag == tagUsedRelocated { + link = b2h(buf) + if link == 0 { + err = &ErrILSEQ{Type: ErrNullReloc, Off: h2off(h)} + log(err) + return + } + + if link > totalAtoms { // invalid last block + err = &ErrILSEQ{Type: ErrRelocBeyondEOF, Off: h2off(h), Arg: link} + log(err) + return + } + } + + return +} + +var nolog = func(error) bool { return false } + +// Verify attempts to find any structural errors in a Filer wrt the +// organization of it as defined by Allocator. 'bitmap' is a scratch pad for +// necessary bookkeeping and will grow to at most to Allocator's +// Filer.Size()/128 (0,78%). Any problems found are reported to 'log' except +// non verify related errors like disk read fails etc. If 'log' returns false +// or the error doesn't allow to (reliably) continue, the verification process +// is stopped and an error is returned from the Verify function. Passing a nil +// log works like providing a log function always returning false. Any +// non-structural errors, like for instance Filer read errors, are NOT reported +// to 'log', but returned as the Verify's return value, because Verify cannot +// proceed in such cases. Verify returns nil only if it fully completed +// verifying Allocator's Filer without detecting any error. +// +// It is recommended to limit the number reported problems by returning false +// from 'log' after reaching some limit. Huge and corrupted DB can produce an +// overwhelming error report dataset. +// +// The verifying process will scan the whole DB at least 3 times (a trade +// between processing space and time consumed). It doesn't read the content of +// free blocks above the head/tail info bytes. If the 3rd phase detects lost +// free space, then a 4th scan (a faster one) is performed to precisely report +// all of them. +// +// If the DB/Filer to be verified is reasonably small, respective if its +// size/128 can comfortably fit within process's free memory, then it is +// recommended to consider using a MemFiler for the bit map. +// +// Statistics are returned via 'stats' if non nil. The statistics are valid +// only if Verify succeeded, ie. it didn't reported anything to log and it +// returned a nil error. +func (a *Allocator) Verify(bitmap Filer, log func(error) bool, stats *AllocStats) (err error) { + if log == nil { + log = nolog + } + + n, err := bitmap.Size() + if err != nil { + return + } + + if n != 0 { + return &ErrINVAL{"Allocator.Verify: bit map initial size non zero (%d)", n} + } + + var bits int64 + bitMask := [8]byte{1, 2, 4, 8, 16, 32, 64, 128} + byteBuf := []byte{0} + + //DONE + // +performance, this implementation is hopefully correct but _very_ + // naive, probably good as a prototype only. Use maybe a MemFiler + // "cache" etc. + // ---- + // Turns out the OS caching is as effective as it can probably get. + bit := func(on bool, h int64) (wasOn bool, err error) { + m := bitMask[h&7] + off := h >> 3 + var v byte + sz, err := bitmap.Size() + if err != nil { + return + } + + if off < sz { + if n, err := bitmap.ReadAt(byteBuf, off); n != 1 { + return false, &ErrILSEQ{Type: ErrOther, Off: off, More: fmt.Errorf("Allocator.Verify - reading bitmap: %s", err)} + } + + v = byteBuf[0] + } + switch wasOn = v&m != 0; on { + case true: + if !wasOn { + v |= m + bits++ + } + case false: + if wasOn { + v ^= m + bits-- + } + } + byteBuf[0] = v + if n, err := bitmap.WriteAt(byteBuf, off); n != 1 || err != nil { + return false, &ErrILSEQ{Type: ErrOther, Off: off, More: fmt.Errorf("Allocator.Verify - writing bitmap: %s", err)} + } + + return + } + + // Phase 1 - sequentially scan a.f to reliably determine block + // boundaries. Set a bit for every block start. + var ( + buf, ubuf [maxRq]byte + prevH, h, atoms int64 + wasOn bool + tag byte + st = AllocStats{ + AllocMap: map[int64]int64{}, + FreeMap: map[int64]int64{}, + } + dlen int + ) + + fsz, err := a.f.Size() + if err != nil { + return + } + + ok := fsz%16 == 0 + totalAtoms := (fsz - fltSz) / atomLen + if !ok { + err = &ErrILSEQ{Type: ErrFileSize, Name: a.f.Name(), Arg: fsz} + log(err) + return + } + + st.TotalAtoms = totalAtoms + prevTag := -1 + lastH := int64(-1) + + for h = 1; h <= totalAtoms; h += atoms { + prevH = h // For checking last block == used + + off := h2off(h) + if err = a.read(buf[:1], off); err != nil { + return + } + + switch tag = buf[0]; tag { + default: // Short used + fallthrough + case tagUsedLong, tagUsedRelocated: + var compressed bool + if compressed, dlen, atoms, _, err = a.verifyUsed(h, totalAtoms, tag, buf[:], ubuf[:], log, false); err != nil { + return + } + + if compressed { + st.Compression++ + } + st.AllocAtoms += atoms + switch { + case tag == tagUsedRelocated: + st.AllocMap[1]++ + st.Relocations++ + default: + st.AllocMap[atoms]++ + st.AllocBytes += int64(dlen) + st.Handles++ + } + case tagFreeShort, tagFreeLong: + if prevTag == tagFreeShort || prevTag == tagFreeLong { + err = &ErrILSEQ{Type: ErrAdjacentFree, Off: h2off(lastH), Arg: off} + log(err) + return + } + + if atoms, _, _, err = a.verifyUnused(h, totalAtoms, tag, log, false); err != nil { + return + } + + st.FreeMap[atoms]++ + st.FreeAtoms += atoms + } + + if wasOn, err = bit(true, h); err != nil { + return + } + + if wasOn { + panic("internal error") + } + + prevTag = int(tag) + lastH = h + } + + if totalAtoms != 0 && (tag == tagFreeShort || tag == tagFreeLong) { + err = &ErrILSEQ{Type: ErrFreeTailBlock, Off: h2off(prevH)} + log(err) + return + } + + // Phase 2 - check used blocks, turn off the map bit for every used + // block. + for h = 1; h <= totalAtoms; h += atoms { + off := h2off(h) + if err = a.read(buf[:1], off); err != nil { + return + } + + var link int64 + switch tag = buf[0]; tag { + default: // Short used + fallthrough + case tagUsedLong, tagUsedRelocated: + if _, _, atoms, link, err = a.verifyUsed(h, totalAtoms, tag, buf[:], ubuf[:], log, true); err != nil { + return + } + case tagFreeShort, tagFreeLong: + if atoms, _, _, err = a.verifyUnused(h, totalAtoms, tag, log, true); err != nil { + return + } + } + + turnoff := true + switch tag { + case tagUsedRelocated: + if err = a.read(buf[:1], h2off(link)); err != nil { + return + } + + switch linkedTag := buf[0]; linkedTag { + case tagFreeShort, tagFreeLong, tagUsedRelocated: + err = &ErrILSEQ{Type: ErrInvalidRelocTarget, Off: off, Arg: link} + log(err) + return + } + + case tagFreeShort, tagFreeLong: + turnoff = false + } + + if !turnoff { + continue + } + + if wasOn, err = bit(false, h); err != nil { + return + } + + if !wasOn { + panic("internal error") + } + + } + + // Phase 3 - using the flt check heads link to proper free blocks. For + // every free block, walk the list, verify the {next, prev} links and + // turn the respective map bit off. After processing all free lists, + // the map bits count should be zero. Otherwise there are "lost" free + // blocks. + + var prev, next, fprev, fnext int64 + rep := a.flt + + for _, list := range rep { + prev, next = 0, list.head + for ; next != 0; prev, next = next, fnext { + if wasOn, err = bit(false, next); err != nil { + return + } + + if !wasOn { + err = &ErrILSEQ{Type: ErrFLT, Off: h2off(next), Arg: h} + log(err) + return + } + + off := h2off(next) + if err = a.read(buf[:1], off); err != nil { + return + } + + switch tag = buf[0]; tag { + default: + panic("internal error") + case tagFreeShort, tagFreeLong: + if atoms, fprev, fnext, err = a.verifyUnused(next, totalAtoms, tag, log, true); err != nil { + return + } + + if min := list.minSize; atoms < min { + err = &ErrILSEQ{Type: ErrFLTSize, Off: h2off(next), Arg: atoms, Arg2: min} + log(err) + return + } + + if fprev != prev { + err = &ErrILSEQ{Type: ErrFreeChaining, Off: h2off(next)} + log(err) + return + } + } + } + + } + + if bits == 0 { // Verify succeeded + if stats != nil { + *stats = st + } + return + } + + // Phase 4 - if after phase 3 there are lost free blocks, report all of + // them to 'log' + for i := range ubuf { // setup zeros for compares + ubuf[i] = 0 + } + + var off, lh int64 + rem, err := bitmap.Size() + if err != nil { + return err + } + + for rem != 0 { + rq := int(mathutil.MinInt64(64*1024, rem)) + var n int + if n, err = bitmap.ReadAt(buf[:rq], off); n != rq { + return &ErrILSEQ{Type: ErrOther, Off: off, More: fmt.Errorf("bitmap ReadAt(size %d, off %#x): %s", rq, off, err)} + } + + if !bytes.Equal(buf[:rq], ubuf[:rq]) { + for d, v := range buf[:rq] { + if v != 0 { + for i, m := range bitMask { + if v&m != 0 { + lh = 8*(off+int64(d)) + int64(i) + err = &ErrILSEQ{Type: ErrLostFreeBlock, Off: h2off(lh)} + log(err) + return + } + } + } + } + } + + off += int64(rq) + rem -= int64(rq) + } + + return +} + +type fltSlot struct { + head int64 + minSize int64 +} + +func (f fltSlot) String() string { + return fmt.Sprintf("head %#x, minSize %#x\n", f.head, f.minSize) +} + +type flt [14]fltSlot + +func (f *flt) init() { + sz := 1 + for i := range *f { + f[i].minSize, f[i].head = int64(sz), 0 + sz <<= 1 + } + f[13].minSize = 4112 +} + +func (f *flt) load(fi Filer, off int64) (err error) { + b := bufs.GCache.Get(fltSz) + defer bufs.GCache.Put(b) + if _, err = fi.ReadAt(b[:], off); err != nil { + return + } + + for i := range *f { + off := 8*i + 1 + f[i].head = b2h(b[off:]) + } + return +} + +func (f *flt) find(rq int) (h int64) { + switch { + case rq < 1: + panic(rq) + case rq >= maxFLTRq: + h, f[13].head = f[13].head, 0 + return + default: + g := f[mathutil.Log2Uint16(uint16(rq)):] + for i := range g { + p := &g[i] + if rq <= int(p.minSize) { + if h = p.head; h != 0 { + p.head = 0 + return + } + } + } + return + } +} + +func (f *flt) head(atoms int64) (h int64) { + switch { + case atoms < 1: + panic(atoms) + case atoms >= maxFLTRq: + return f[13].head + default: + lg := mathutil.Log2Uint16(uint16(atoms)) + g := f[lg:] + for i := range g { + if atoms < g[i+1].minSize { + return g[i].head + } + } + panic("internal error") + } +} + +func (f *flt) setHead(h, atoms int64, fi Filer) (err error) { + switch { + case atoms < 1: + panic(atoms) + case atoms >= maxFLTRq: + b := bufs.GCache.Get(7) + defer bufs.GCache.Put(b) + if _, err = fi.WriteAt(h2b(b[:], h), 8*13+1); err != nil { + return + } + + f[13].head = h + return + default: + lg := mathutil.Log2Uint16(uint16(atoms)) + g := f[lg:] + for i := range f { + if atoms < g[i+1].minSize { + b := bufs.GCache.Get(7) + defer bufs.GCache.Put(b) + if _, err = fi.WriteAt(h2b(b[:], h), 8*int64(i+lg)+1); err != nil { + return + } + + g[i].head = h + return + } + } + panic("internal error") + } +} + +func (f *flt) String() string { + a := []string{} + for i, v := range *f { + a = append(a, fmt.Sprintf("[%2d] %s", i, v)) + } + return strings.Join(a, "") +} + +type node struct { + b []byte + h int64 + prev, next *node +} + +type cache []*node + +func (c *cache) get(n int) *node { + r, _ := c.get2(n) + return r +} + +func (c *cache) get2(n int) (r *node, isZeroed bool) { + s := *c + lens := len(s) + if lens == 0 { + return &node{b: make([]byte, n, mathutil.Min(2*n, maxBuf))}, true + } + + i := sort.Search(lens, func(x int) bool { return len(s[x].b) >= n }) + if i == lens { + i-- + s[i].b, isZeroed = make([]byte, n, mathutil.Min(2*n, maxBuf)), true + } + + r = s[i] + r.b = r.b[:n] + copy(s[i:], s[i+1:]) + s = s[:lens-1] + *c = s + return +} + +func (c *cache) cget(n int) (r *node) { + r, ok := c.get2(n) + if ok { + return + } + + for i := range r.b { + r.b[i] = 0 + } + return +} + +func (c *cache) size() (sz int64) { + for _, n := range *c { + sz += int64(cap(n.b)) + } + return +} + +func (c *cache) put(n *node) *node { + s := *c + n.b = n.b[:cap(n.b)] + lenb := len(n.b) + lens := len(s) + i := sort.Search(lens, func(x int) bool { return len(s[x].b) >= lenb }) + s = append(s, nil) + copy(s[i+1:], s[i:]) + s[i] = n + *c = s + return n +} + +type lst struct { + front, back *node +} + +func (l *lst) pushFront(n *node) *node { + if l.front == nil { + l.front, l.back, n.prev, n.next = n, n, nil, nil + return n + } + + n.prev, n.next, l.front.prev, l.front = nil, l.front, n, n + return n +} + +func (l *lst) remove(n *node) *node { + if n.prev == nil { + l.front = n.next + } else { + n.prev.next = n.next + } + if n.next == nil { + l.back = n.prev + } else { + n.next.prev = n.prev + } + n.prev, n.next = nil, nil + return n +} + +func (l *lst) removeBack() *node { + return l.remove(l.back) +} + +func (l *lst) moveToFront(n *node) *node { + return l.pushFront(l.remove(n)) +} + +func (l *lst) size() (sz int64) { + for n := l.front; n != nil; n = n.next { + sz += int64(cap(n.b)) + } + return +} + +func cacheAudit(m map[int64]*node, l *lst) (err error) { + cnt := 0 + for h, n := range m { + if g, e := n.h, h; g != e { + return fmt.Errorf("cacheAudit: invalid node handle %d != %d", g, e) + } + + if cnt, err = l.audit(n, true); err != nil { + return + } + } + + if g, e := cnt, len(m); g != e { + return fmt.Errorf("cacheAudit: invalid cache size %d != %d", g, e) + } + + return +} + +func (l *lst) audit(n *node, onList bool) (cnt int, err error) { + if !onList && (n.prev != nil || n.next != nil) { + return -1, fmt.Errorf("lst.audit: free node with non nil linkage") + } + + if l.front == nil && l.back != nil || l.back == nil && l.front != nil { + return -1, fmt.Errorf("lst.audit: one of .front/.back is nil while the other is non nil") + } + + if l.front == l.back && l.front != nil { + x := l.front + if x.prev != nil || x.next != nil { + return -1, fmt.Errorf("lst.audit: single node has non nil linkage") + } + + if onList && x != n { + return -1, fmt.Errorf("lst.audit: single node is alien") + } + } + + seen := false + var prev *node + x := l.front + for x != nil { + cnt++ + if x.prev != prev { + return -1, fmt.Errorf("lst.audit: broken .prev linkage") + } + + if x == n { + seen = true + } + + prev = x + x = x.next + } + + if prev != l.back { + return -1, fmt.Errorf("lst.audit: broken .back linkage") + } + + if onList && !seen { + return -1, fmt.Errorf("lst.audit: node missing in list") + } + + if !onList && seen { + return -1, fmt.Errorf("lst.audit: node should not be on the list") + } + + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/falloc_test.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/falloc_test.go new file mode 100644 index 000000000..f4f9ba21f --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/falloc_test.go @@ -0,0 +1,1833 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lldb + +import ( + "bytes" + "encoding/hex" + "flag" + "fmt" + "math/rand" + "os" + "runtime" + "sort" + "strings" + "testing" + "time" + + "github.com/cznic/bufs" + "github.com/cznic/sortutil" + "github.com/cznic/zappy" +) + +var ( + allocRndTestLimit = flag.Uint("lim", 2*maxShort, "Allocator rnd test initial blocks size limit") + allocRndTestHardLimit = flag.Uint("hlim", 0, "Allocator rnd test initial blocks size hard limit") + testN = flag.Int("N", 128, "Allocator rnd test block count") + allocRndDump = flag.Bool("dump", false, "Produce dump files on TestAllocatorRnd crash") + oKeep = flag.Bool("keep", false, "do not delete testing DB/WAL (where applicable)") +) + +func init() { + reallocTestHook = true +} + +func mfBytes(f Filer) []byte { + var b bytes.Buffer + if _, err := f.(*MemFiler).WriteTo(&b); err != nil { + panic(err) + } + + return b.Bytes() +} + +// Paranoid Allocator, automatically verifies whenever possible. +type pAllocator struct { + *Allocator + errors []error + logger func(error) bool + lastKnownGood *MemFiler + lastKnownGoodFLT flt + lastOp string + stats AllocStats +} + +func newPAllocator(f Filer) (*pAllocator, error) { + a, err := NewAllocator(f, &Options{}) + if err != nil { + return nil, err + } + + r := &pAllocator{Allocator: a, lastKnownGood: NewMemFiler()} + r.logger = func(err error) bool { + r.errors = append(r.errors, err) + return len(r.errors) < 100 + } + + return r, nil +} + +func (a *pAllocator) err() error { + var n int + if n = len(a.errors); n == 0 { + return nil + } + + s := make([]string, n) + for i, e := range a.errors { + s[i] = e.Error() + } + return fmt.Errorf("\n%s", strings.Join(s, "\n")) +} + +func (a *pAllocator) preMortem(s string) { + var e error + if e := a.lastKnownGood.Truncate(0); e != nil { + panic(e) + } + b := mfBytes(a.Allocator.f) + if _, e = a.lastKnownGood.WriteAt(b, 0); e != nil { + return + } + a.lastKnownGoodFLT = a.flt + a.lastOp = s +} + +func (a *pAllocator) Alloc(b []byte) (handle int64, err error) { + if *allocRndDump { + a.preMortem("") + defer func() { a.lastOp = fmt.Sprintf("Alloc(%d bytes): h %#x", len(b), handle) }() + } + + if handle, err = a.Allocator.Alloc(b); err != nil { + return + } + + if err = a.Allocator.Verify(NewMemFiler(), a.logger, &a.stats); err != nil { + err = fmt.Errorf("'%s': %v", err, a.err()) + return + } + + err = a.err() + return +} + +func (a *pAllocator) Free(handle int64) (err error) { + if *allocRndDump { + a.preMortem(fmt.Sprintf("Free(h %#x)", handle)) + } + + if err = a.Allocator.Free(handle); err != nil { + return + } + + if err = a.Allocator.Verify(NewMemFiler(), a.logger, &a.stats); err != nil { + err = fmt.Errorf("'%s': %v", err, a.err()) + return + } + + err = a.err() + return +} + +func (a *pAllocator) Realloc(handle int64, b []byte) (err error) { + if *allocRndDump { + a.preMortem(fmt.Sprintf("Realloc(h %#x, %d bytes)", handle, len(b))) + } + + if err = a.Allocator.Realloc(handle, b); err != nil { + return + } + + if err = cacheAudit(a.Allocator.m, &a.Allocator.lru); err != nil { + return + } + + if err = a.Allocator.Verify(NewMemFiler(), a.logger, &a.stats); err != nil { + err = fmt.Errorf("'%s': %v", err, a.err()) + return + } + + err = a.err() + return +} + +func dump(a *pAllocator, t *testing.T) { + m := a.f.(*MemFiler) + sz, err := m.Size() + if err != nil { + t.Fatal(err) + } + + t.Logf("MemFiler.Size() == %d(%#x)", sz, sz) + if !*allocRndDump { + return + } + + fn := "good-dump" + f, err := os.Create(fn) + if err != nil { + t.Fatal(err) + } + + defer f.Close() + sz, err = a.lastKnownGood.WriteTo(f) + if err != nil { + t.Error(err) + return + } + + t.Logf("%d(%#x) writen to %q", sz, sz, fn) + + fn = "bad-dump" + g, err := os.Create(fn) + if err != nil { + t.Fatal(err) + } + + defer g.Close() + sz, err = m.WriteTo(g) + if err != nil { + t.Error(err) + return + } + + t.Logf("%d(%#x) writen to %q", sz, sz, fn) + + t.Log("Last known good FLT") + for _, slot := range a.lastKnownGoodFLT { + if h := slot.head; h != 0 { + t.Logf("min %d head %#x off %#x", slot.minSize, h, h2off(h)) + } + } + + t.Log("Current FLT") + r := a.flt + for _, slot := range r { + if h := slot.head; h != 0 { + t.Logf("min %d head %#x off %#x", slot.minSize, h, h2off(h)) + } + } + t.Logf("Last op: %q", a.lastOp) +} + +func init() { + if *testN <= 0 { + *testN = 1 + } +} + +func TestVerify0(t *testing.T) { + // All must fail + tab := []string{ + + // 0: Reloc, links beyond EOF + "" + + "fd 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00", + // 1: Reloc, links beyond EOF + "" + + "fd 00 00 00 00 00 00 03 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 2: Reloc, broken target + "" + + "fd 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 3: Free block at file tail + "" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe", + // 4: Free block at file tail + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe", + // 5: Reloc, invalid target 0xfe + "" + + "fd 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 6: Reloc, invalid target 0xfd + "" + + "fd 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "fd 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 7: Lost free block @ 0x00 + "" + + "fe 00 00 00 00 00 00 02 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 8: Lost free block @ 0x10 + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 02 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 9: Invalid padding + "" + + "00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 10: Invalid padding + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00", + // 11: Invalid padding + "" + + "01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 12: Invalid padding + "" + + "01 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00", + // 13: Invalid padding + "" + + "0d 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00", + // 14: Invalid CC (tail tag) + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02", + // 15: Invalid CC (tail tag) + "" + + "fd 00 00 00 00 00 00 02 00 00 00 00 00 00 00 01", + // 16: Cannot decompress + "" + + "0e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01", + // 17: Invalid reloc target + "" + + "fd 00 00 00 00 00 00 03 00 00 00 00 00 00 00 00" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 18: Invalid tail tag @1 + "" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 19: Invalid size @1 + "" + + "ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 20: Invalid size @1 + "" + + "ff 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 21: Invalid size @1 + "" + + "ff 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 22: Invalid .next @1 + "" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 04 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 23: Invalid .prev @1 + "" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 04 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 24: Invalid tail tag @1 + "" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 25: Invalid tail size @1 + "" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 26: Invalid tail size @1 + "" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 27: Invalid tail size @1 + "" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f + } + + for i, test := range tab { + errors := []error{} + + f := NewMemFiler() + b := s2b(test) + b = append(make([]byte, fltSz), b...) + n := len(b) + if n == 0 { + t.Fatal(n) + } + + if m, err := f.ReadFrom(bytes.NewBuffer(b)); m != int64(n) || err != nil { + t.Fatal(m, err) + } + + sz, err := f.Size() + if err != nil { + t.Fatal(err) + } + + if g, e := sz, int64(n); g != e { + t.Fatal(g, e) + } + + a, err := newPAllocator(f) + if err != nil { + t.Fatal(err) + } + + err = a.Verify( + NewMemFiler(), + func(err error) bool { + if err == nil { + t.Fatal("nil error") + } + errors = append(errors, err) + return false + }, + nil, + ) + if err == nil { + t.Fatal(i, "unexpected success") + } + + t.Log(i, err, errors) + } +} + +func TestVerify1(t *testing.T) { + f := NewMemFiler() + bitmap := NewMemFiler() + if n, err := bitmap.WriteAt([]byte{0}, 0); n != 1 || err != nil { + t.Fatal(n, err) + } + + a, err := newPAllocator(f) + if err != nil { + t.Fatal(err) + } + + if err := a.Verify( + bitmap, + func(error) bool { + panic("intrnal error") + }, + nil, + ); err == nil { + t.Fatal("unexpected success") + } +} + +func repDump(a flt) string { + b := []string{} + for _, v := range a { + if h := v.head; h != 0 { + b = append(b, fmt.Sprintf("min:%d, h:%d", v.minSize, h)) + } + } + return strings.Join(b, ";") +} + +func TestVerify2(t *testing.T) { + // All must fail for the fixed (see bellow) FLT.Report() + tab := []string{ + + // 0: FLT broken linkage (missing free blocks @2,4) + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 1: FLT broken linkage (missing free block @4) + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 2: bad size @4 + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // // 3: bad size @4 + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 04 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "ff 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // // 4: bad .next @6 from @2 + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 06 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // // 5: bad .prev @7 + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 07 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // // 6: bad .next @7 + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 07 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 02 00 00 00 00 00 00 07 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // // 7: bad .next @5 + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 07 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 02 00 00 00 00 00 00 01 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // // 8: bad chaining + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 07 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 02 00 00 00 00 00 00 01 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // // 9: lost free block @8 + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 0f fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "ff 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ff" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + + "fe 00 00 00 00 00 00 02 00 00 00 00 00 00 00 fe" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + // 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f + } + + for i, test := range tab { + errors := []error{} + + f := NewMemFiler() + b := s2b(test) + b = append(make([]byte, fltSz), b...) + n := len(b) + if n == 0 { + t.Fatal(n) + } + + if m, err := f.ReadFrom(bytes.NewBuffer(b)); m != int64(n) || err != nil { + t.Fatal(m, err) + } + + sz, err := f.Size() + if err != nil { + t.Fatal(err) + } + + if g, e := sz, int64(n); g != e { + t.Fatal(g, e) + } + + a, err := newPAllocator(f) + if err != nil { + t.Fatal(err) + } + + a.flt.setHead(2, 1, a.f) + a.flt.setHead(4, 2, a.f) + err = a.Verify( + NewMemFiler(), + func(err error) bool { + if err == nil { + t.Fatal("nil error") + } + t.Log(i, "logged: ", err) + errors = append(errors, err) + return true + }, + nil, + ) + if err == nil { + t.Fatal(i, "unexpected success") + } + + t.Log(i, err, errors) + } +} + +// Allocation in an empty DB. +func TestAllocatorAlloc0(t *testing.T) { + tab := []struct { + h int64 + b, f, fc string + }{ + {1, // len 0 + "" + + "", + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + "" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}, + {1, // len 1 + "" + + "42", + "" + + "01 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + "" + + "01 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}, + {1, // max single atom, not compressible + "" + + "01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e", + "" + + "0e 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 00", + "" + + "0e 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 00"}, + {1, // max single atom, compressible, but not eligible for it + "" + + "01 02 03 04 05 06 07 08 99 01 02 03 04 05", + "" + + "0e 01 02 03 04 05 06 07 08 99 01 02 03 04 05 00", + "" + + "0e 01 02 03 04 05 06 07 08 99 01 02 03 04 05 00"}, + {1, // > 1 atom, not compressible + "" + + "01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f", + "" + + "0f 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + "" + + "0f 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}, + {1, // > 1 atom, compressible + "" + + "01 02 03 04 05 06 07 08 99 01 02 03 04 05 06 07" + + "08", + "" + + "11 01 02 03 04 05 06 07 08 99 01 02 03 04 05 06" + + "07 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00", + "" + + "0e 11 12 01 02 03 04 05 06 07 08 99 01 0d 09 01"}, + {1, // longest short + "" + + "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "10 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "20 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "30 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "40 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "50 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "60 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "70 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "80 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "90 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "a0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "b0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "c0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "d0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "e0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "f0 01 02 03 04 05 06 07 08 09 0a", + "" + + "" + + "fb 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f 10 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f 20 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f 30 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f 40 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f 50 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f 60 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f 70 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f 80 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f 90 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f a0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f b0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f c0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f d0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f e0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e" + + "0f f0 01 02 03 04 05 06 07 08 09 0a 00 00 00 00", + "" + + "" + + "4e fb 01 20 00 01 02 03 04 05 06 07 08 09 0a 0b" + + "0c 0d 0e 0f 10 1d 10 00 20 1d 10 00 30 1d 10 00" + + "40 1d 10 00 50 1d 10 00 60 1d 10 00 70 1d 10 00" + + "80 1d 10 00 90 1d 10 00 a0 1d 10 00 b0 1d 10 00" + + "c0 1d 10 00 d0 1d 10 00 e0 1d 10 00 f0 13 10 01"}, + + {1, // shortest long + "" + + "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "10 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "20 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "30 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "40 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "50 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "60 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "70 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "80 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "90 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "a0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "b0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "c0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "d0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "e0 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" + + "f0 01 02 03 04 05 06 07 08 09 0a 0b", + "" + + "" + + "fc 00 fc 00 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f 10 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f 20 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f 30 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f 40 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f 50 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f 60 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f 70 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f 80 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f 90 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f a0 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f b0 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f c0 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f d0 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f e0 01 02 03 04 05 06 07 08 09 0a 0b 0c" + + "0d 0e 0f f0 01 02 03 04 05 06 07 08 09 0a 0b 00", + "" + + "" + + "4e fc 01 20 00 01 02 03 04 05 06 07 08 09 0a 0b" + + "0c 0d 0e 0f 10 1d 10 00 20 1d 10 00 30 1d 10 00" + + "40 1d 10 00 50 1d 10 00 60 1d 10 00 70 1d 10 00" + + "80 1d 10 00 90 1d 10 00 a0 1d 10 00 b0 1d 10 00" + + "c0 1d 10 00 d0 1d 10 00 e0 1d 10 00 f0 15 10 01"}, + } + + for i, test := range tab { + f := func(compress bool, e []byte) { + f := NewMemFiler() + a, err := newPAllocator(f) + if err != nil { + t.Fatal(err) + } + + a.Compress = compress + h, err := a.Alloc(s2b(test.b)) + if err != nil { + t.Fatalf("%d %#v\n%s", i, err, hex.Dump(mfBytes(f))) + } + + if g, e := h, test.h; g != e { + t.Fatal(i, g, e) + } + + g := mfBytes(f) + if g = g[fltSz:]; !bytes.Equal(g, e) { + t.Fatalf("\ni: %d compress: %t\ng:\n%se:\n%s", i, compress, hex.Dump(g), hex.Dump(e)) + } + } + f(false, s2b(test.f)) + f(true, s2b(test.fc)) + } +} + +func TestAllocatorMakeUsedBlock(t *testing.T) { + f := NewMemFiler() + a, err := NewAllocator(f, &Options{}) + if err != nil { + t.Fatal(err) + } + + dst := bufs.GCache.Get(zappy.MaxEncodedLen(maxRq + 1)) + defer bufs.GCache.Put(dst) + if _, _, _, err := a.makeUsedBlock(dst, make([]byte, maxRq)); err != nil { + t.Fatal(err) + } + + if _, _, _, err := a.makeUsedBlock(dst, make([]byte, maxRq+1)); err == nil { + t.Fatal("unexpected success") + } +} + +func stableRef(m map[int64][]byte) (r []struct { + h int64 + b []byte +}) { + a := make(sortutil.Int64Slice, 0, len(m)) + for k := range m { + a = append(a, k) + } + sort.Sort(a) + for _, v := range a { + r = append(r, struct { + h int64 + b []byte + }{v, m[v]}) + } + return +} + +func TestAllocatorRnd(t *testing.T) { + N := *testN + + for cc := 0; cc < 2; cc++ { + rng := rand.New(rand.NewSource(42)) + f := NewMemFiler() + a, err := newPAllocator(f) + if err != nil { + t.Fatal(err) + } + + balance := 0 + + bad := func() bool { + if a.Compress { + return false + } + + actual := a.stats.TotalAtoms - a.stats.FreeAtoms - a.stats.Relocations + if int64(balance) != actual { + t.Logf("balance: %d, actual %d\n%#v", balance, actual, a.stats) + return true + } + + return false + } + + if cc != 0 { + a.Compress = true + } + ref := map[int64][]byte{} + + for pass := 0; pass < 2; pass++ { + + // A) Alloc N blocks + for i := 0; i < N; i++ { + rq := rng.Int31n(int32(*allocRndTestLimit)) + if rq%127 == 0 { + rq = 3 * maxRq / 4 + } + if rq%11 == 0 { + rq %= 23 + } + if hl := *allocRndTestHardLimit; hl != 0 { + rq = rq % int32(hl) + } + b := make([]byte, rq) + for j := range b { + b[j] = byte(rng.Int()) + } + if rq > 300 { + for i := 100; i < 200; i++ { + b[i] = 'A' // give compression a chance + } + } + + balance += n2atoms(len(b)) + h, err := a.Alloc(b) + if err != nil || bad() { + dump(a, t) + t.Fatalf( + "A) N %d, kind %d, pass %d, i:%d, len(b):%d(%#x), err %v", + N, 0, pass, i, len(b), len(b), err, + ) + } + + ref[h] = b + } + + var rb []byte + + // B) Check them back + for h, wb := range ref { + if rb, err = a.Get(rb, h); err != nil { + dump(a, t) + t.Fatal("B)", err) + } + + if !bytes.Equal(rb, wb) { + dump(a, t) + t.Fatalf("B) h %d", h) + } + } + + nf := 0 + // C) Free every third block + for _, v := range stableRef(ref) { + h, b := v.h, v.b + if rng.Int()%3 != 0 { + continue + } + + balance -= n2atoms(len(b)) + if err = a.Free(h); err != nil || bad() { + dump(a, t) + t.Fatal(err) + } + + delete(ref, h) + nf++ + } + + // D) Check them back + for h, wb := range ref { + if rb, err = a.Get(rb, h); err != nil { + dump(a, t) + t.Fatal("D)", err) + } + + if !bytes.Equal(rb, wb) { + dump(a, t) + t.Fatalf("D) h %d", h) + } + } + + // E) Resize every block remaining + for _, v := range stableRef(ref) { + h, wb := v.h, append([]byte(nil), v.b...) + len0 := len(wb) + switch rng.Int() & 1 { + case 0: + wb = wb[:len(wb)*3/4] + case 1: + wb = append(wb, wb...) + } + if len(wb) > maxRq { + wb = wb[:maxRq] + } + + for j := range wb { + wb[j] = byte(rng.Int()) + } + if len(wb) > 300 { + for i := 100; i < 200; i++ { + wb[i] = 'D' // give compression a chance + } + } + a0, a1 := n2atoms(len0), n2atoms(len(wb)) + balance = balance - a0 + a1 + if err := a.Realloc(h, wb); err != nil || bad() { + dump(a, t) + t.Fatalf( + "D) h:%#x, len(b):%#4x, len(wb): %#x, err %v", + h, len0, len(wb), err, + ) + } + + if err = cacheAudit(a.m, &a.lru); err != nil { + t.Fatal(err) + } + + ref[h] = wb + } + + // F) Check them back + for h, wb := range ref { + if rb, err = a.Get(rb, h); err != nil { + dump(a, t) + t.Fatal("E)", err) + } + + if !bytes.Equal(rb, wb) { + dump(a, t) + t.Fatalf("E) h %d", h) + } + } + } + + if cc == 0 { + sz, err := f.Size() + if err != nil { + t.Fatal(err) + } + + t.Logf( + "kind %d, AllocAtoms %7d, AllocBytes %7d, FreeAtoms %7d, Relocations %7d, TotalAtoms %7d, f.Size %7d, space eff %.2f%%", + 0, a.stats.AllocAtoms, a.stats.AllocBytes, a.stats.FreeAtoms, a.stats.Relocations, a.stats.TotalAtoms, sz, 100*float64(a.stats.AllocBytes)/float64(sz), + ) + } + // Free everything + for h, b := range ref { + balance -= n2atoms(len(b)) + if err = a.Free(h); err != nil || bad() { + dump(a, t) + t.Fatal(err) + } + } + + sz, err := a.f.Size() + if err != nil { + t.Fatal(err) + } + + if g, e := sz, int64(fltSz); g != e { + dump(a, t) + t.Fatal(g, e) + } + } +} + +func TestRollbackAllocator(t *testing.T) { + f := NewMemFiler() + var r *RollbackFiler + r, err := NewRollbackFiler(f, + func(sz int64) (err error) { + if err = f.Truncate(sz); err != nil { + return err + } + + return f.Sync() + }, + f, + ) + if err != nil { + t.Fatal(err) + } + + if err := r.BeginUpdate(); err != nil { // BeginUpdate 0->1 + t.Fatal(err) + } + + a, err := NewAllocator(r, &Options{}) + if err != nil { + t.Fatal(err) + } + + h, err := a.Alloc(nil) + if err != nil { + t.Fatal(err) + } + + if h != 1 { + t.Fatal(h) + } + + // | 1 | + + h, err = a.Alloc(nil) + if err != nil { + t.Fatal(err) + } + + if h != 2 { + t.Fatal(h) + } + + // | 1 | 2 | + h, err = a.Alloc(nil) + if err != nil { + t.Fatal(err) + } + + if h != 3 { + t.Fatal(h) + } + + // | 1 | 2 | 3 | + if err = a.Free(2); err != nil { + t.Fatal(err) + } + + // | 1 | free | 3 | + if err := r.BeginUpdate(); err != nil { // BeginUpdate 1->2 + t.Fatal(err) + } + + h, err = a.Alloc(nil) + if err != nil { + t.Fatal(err) + } + + if h != 2 { + t.Fatal(h) + } + + // | 1 | 2 | 3 | + if err := r.Rollback(); err != nil { // Rollback 2->1 + t.Fatal(err) + } + + // | 1 | free | 3 | + h, err = a.Alloc(nil) + if err != nil { + t.Fatal(err) + } + + if h != 2 { + t.Fatal(h) + } + + // | 1 | 2 | 3 | + if err := a.Verify(NewMemFiler(), nil, nil); err != nil { + t.Fatal(err) + } +} + +func benchmarkAllocatorAlloc(b *testing.B, f Filer, sz int) { + if err := f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + a, err := NewAllocator(f, &Options{}) + if err != nil { + b.Error(err) + return + } + + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + + v := make([]byte, sz) + runtime.GC() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err = f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + if h, err := a.Alloc(v); h <= 0 || err != nil { + f.EndUpdate() + b.Error(h, err) + return + } + + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + } +} + +func benchmarkAllocatorAllocMemFiler(b *testing.B, sz int) { + f := NewMemFiler() + benchmarkAllocatorAlloc(b, f, sz) +} + +func BenchmarkAllocatorAllocMemFiler1e0(b *testing.B) { + benchmarkAllocatorAllocMemFiler(b, 0) +} + +func BenchmarkAllocatorAllocMemFiler1e1(b *testing.B) { + benchmarkAllocatorAllocMemFiler(b, 1e1) +} + +func BenchmarkAllocatorAllocMemFiler1e2(b *testing.B) { + benchmarkAllocatorAllocMemFiler(b, 1e2) +} + +func BenchmarkAllocatorAllocMemFiler1e3(b *testing.B) { + benchmarkAllocatorAllocMemFiler(b, 1e3) +} + +func benchmarkAllocatorAllocSimpleFileFiler(b *testing.B, sz int) { + dir, testDbName := temp() + defer os.RemoveAll(dir) + + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer f.Close() + + benchmarkAllocatorAlloc(b, NewSimpleFileFiler(f), sz) +} + +func BenchmarkAllocatorAllocSimpleFileFiler0(b *testing.B) { + benchmarkAllocatorAllocSimpleFileFiler(b, 0) +} + +func BenchmarkAllocatorAllocSimpleFileFiler1e1(b *testing.B) { + benchmarkAllocatorAllocSimpleFileFiler(b, 1e1) +} + +func BenchmarkAllocatorAllocSimpleFileFiler1e2(b *testing.B) { + benchmarkAllocatorAllocSimpleFileFiler(b, 1e2) +} + +func BenchmarkAllocatorAllocSimpleFileFiler1e3(b *testing.B) { + benchmarkAllocatorAllocSimpleFileFiler(b, 1e3) +} + +func benchmarkAllocatorAllocRollbackFiler(b *testing.B, sz int) { + dir, testDbName := temp() + defer os.RemoveAll(dir) + + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer f.Close() + + g := NewSimpleFileFiler(f) + var filer *RollbackFiler + if filer, err = NewRollbackFiler( + g, + func(sz int64) error { + if err = g.Truncate(sz); err != nil { + return err + } + + return g.Sync() + }, + g, + ); err != nil { + b.Error(err) + return + } + + benchmarkAllocatorAlloc(b, filer, sz) +} + +func BenchmarkAllocatorAllocRollbackFiler0(b *testing.B) { + benchmarkAllocatorAllocRollbackFiler(b, 0) +} + +func BenchmarkAllocatorAllocRollbackFiler1e1(b *testing.B) { + benchmarkAllocatorAllocRollbackFiler(b, 1e1) +} + +func BenchmarkAllocatorAllocRollbackFiler1e2(b *testing.B) { + benchmarkAllocatorAllocRollbackFiler(b, 1e2) +} + +func BenchmarkAllocatorAllocRollbackFiler1e3(b *testing.B) { + benchmarkAllocatorAllocRollbackFiler(b, 1e3) +} + +func benchmarkAllocatorAllocACIDFiler(b *testing.B, sz int) { + dir, testDbName := temp() + defer os.RemoveAll(dir) + + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer f.Close() + + wal, err := os.OpenFile(testDbName+".wal", os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer wal.Close() + + filer, err := NewACIDFiler(NewSimpleFileFiler(f), wal) + if err != nil { + b.Error(err) + return + } + + benchmarkAllocatorAlloc(b, filer, sz) +} + +func BenchmarkAllocatorAllocACIDFiler0(b *testing.B) { + benchmarkAllocatorAllocACIDFiler(b, 0) +} + +func BenchmarkAllocatorAllocACIDFiler1e1(b *testing.B) { + benchmarkAllocatorAllocACIDFiler(b, 1e1) +} + +func BenchmarkAllocatorAllocACIDFiler1e2(b *testing.B) { + benchmarkAllocatorAllocACIDFiler(b, 1e2) +} + +func BenchmarkAllocatorAllocACIDFiler1e3(b *testing.B) { + benchmarkAllocatorAllocACIDFiler(b, 1e3) +} + +func benchmarkAllocatorRndFree(b *testing.B, f Filer, sz int) { + if err := f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + a, err := NewAllocator(f, &Options{}) + if err != nil { + b.Error(err) + return + } + + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + + v := make([]byte, sz) + ref := map[int64]struct{}{} + for i := 0; i < b.N; i++ { + if err = f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + h, err := a.Alloc(v) + if h <= 0 || err != nil { + f.EndUpdate() + b.Error(h, err) + return + } + + ref[h] = struct{}{} + + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + } + runtime.GC() + b.ResetTimer() + for h := range ref { + if err = f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + if err = a.Free(h); err != nil { + f.EndUpdate() + b.Error(h, err) + return + } + + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + } +} + +func benchmarkAllocatorRndFreeMemFiler(b *testing.B, sz int) { + f := NewMemFiler() + benchmarkAllocatorRndFree(b, f, sz) +} + +func BenchmarkAllocatorRndFreeMemFiler0(b *testing.B) { + benchmarkAllocatorRndFreeMemFiler(b, 0) +} + +func BenchmarkAllocatorRndFreeMemFiler1e1(b *testing.B) { + benchmarkAllocatorRndFreeMemFiler(b, 1e1) +} + +func BenchmarkAllocatorRndFreeMemFiler1e2(b *testing.B) { + benchmarkAllocatorRndFreeMemFiler(b, 1e2) +} + +func BenchmarkAllocatorRndFreeMemFiler1e3(b *testing.B) { + benchmarkAllocatorRndFreeMemFiler(b, 1e3) +} + +func benchmarkAllocatorRndFreeSimpleFileFiler(b *testing.B, sz int) { + dir, testDbName := temp() + defer os.RemoveAll(dir) + + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer f.Close() + + benchmarkAllocatorRndFree(b, NewSimpleFileFiler(f), sz) +} + +func BenchmarkAllocatorRndFreeSimpleFileFiler0(b *testing.B) { + benchmarkAllocatorRndFreeSimpleFileFiler(b, 0) +} + +func BenchmarkAllocatorRndFreeSimpleFileFiler1e1(b *testing.B) { + benchmarkAllocatorRndFreeSimpleFileFiler(b, 1e1) +} + +func BenchmarkAllocatorRndFreeSimpleFileFiler1e2(b *testing.B) { + benchmarkAllocatorRndFreeSimpleFileFiler(b, 1e2) +} + +func BenchmarkAllocatorRndFreeSimpleFileFiler1e3(b *testing.B) { + benchmarkAllocatorRndFreeSimpleFileFiler(b, 1e3) +} + +func benchmarkAllocatorRndFreeRollbackFiler(b *testing.B, sz int) { + dir, testDbName := temp() + defer os.RemoveAll(dir) + + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer f.Close() + + g := NewSimpleFileFiler(f) + var filer *RollbackFiler + if filer, err = NewRollbackFiler( + g, + func(sz int64) error { + if err = g.Truncate(sz); err != nil { + return err + } + + return g.Sync() + }, + g, + ); err != nil { + b.Error(err) + return + } + + benchmarkAllocatorRndFree(b, filer, sz) +} + +func BenchmarkAllocatorRndFreeRollbackFiler0(b *testing.B) { + benchmarkAllocatorRndFreeRollbackFiler(b, 0) +} + +func BenchmarkAllocatorRndFreeRollbackFiler1e1(b *testing.B) { + benchmarkAllocatorRndFreeRollbackFiler(b, 1e1) +} + +func BenchmarkAllocatorRndFreeRollbackFiler1e2(b *testing.B) { + benchmarkAllocatorRndFreeRollbackFiler(b, 1e2) +} + +func BenchmarkAllocatorRndFreeRollbackFiler1e3(b *testing.B) { + benchmarkAllocatorRndFreeRollbackFiler(b, 1e3) +} + +func benchmarkAllocatorRndFreeACIDFiler(b *testing.B, sz int) { + dir, testDbName := temp() + defer os.RemoveAll(dir) + + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer f.Close() + + wal, err := os.OpenFile(testDbName+".wal", os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer wal.Close() + + filer, err := NewACIDFiler(NewSimpleFileFiler(f), wal) + if err != nil { + b.Error(err) + return + } + + benchmarkAllocatorRndFree(b, filer, sz) +} + +func BenchmarkAllocatorRndFreeACIDFiler0(b *testing.B) { + benchmarkAllocatorRndFreeACIDFiler(b, 0) +} + +func BenchmarkAllocatorRndFreeACIDFiler1e1(b *testing.B) { + benchmarkAllocatorRndFreeACIDFiler(b, 1e1) +} + +func BenchmarkAllocatorRndFreeACIDFiler1e2(b *testing.B) { + benchmarkAllocatorRndFreeACIDFiler(b, 1e2) +} + +func BenchmarkAllocatorRndFreeACIDFiler1e3(b *testing.B) { + benchmarkAllocatorRndFreeACIDFiler(b, 1e3) +} + +func benchmarkAllocatorRndGet(b *testing.B, f Filer, sz int) { + if err := f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + a, err := NewAllocator(f, &Options{}) + if err != nil { + b.Error(err) + return + } + + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + + v := make([]byte, sz) + ref := map[int64]struct{}{} + if err = f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + for i := 0; i < b.N; i++ { + h, err := a.Alloc(v) + if h <= 0 || err != nil { + f.EndUpdate() + b.Error(h, err) + return + } + + ref[h] = struct{}{} + + } + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + + runtime.GC() + b.ResetTimer() + for h := range ref { + if err = f.BeginUpdate(); err != nil { + b.Error(err) + return + } + + if _, err = a.Get(v, h); err != nil { + f.EndUpdate() + b.Error(h, err) + return + } + + if err = f.EndUpdate(); err != nil { + b.Error(err) + return + } + } +} + +func benchmarkAllocatorRndGetMemFiler(b *testing.B, sz int) { + f := NewMemFiler() + benchmarkAllocatorRndGet(b, f, sz) +} + +func BenchmarkAllocatorRndGetMemFiler0(b *testing.B) { + benchmarkAllocatorRndGetMemFiler(b, 0) +} + +func BenchmarkAllocatorRndGetMemFiler1e1(b *testing.B) { + benchmarkAllocatorRndGetMemFiler(b, 1e1) +} + +func BenchmarkAllocatorRndGetMemFiler1e2(b *testing.B) { + benchmarkAllocatorRndGetMemFiler(b, 1e2) +} + +func BenchmarkAllocatorRndGetMemFiler1e3(b *testing.B) { + benchmarkAllocatorRndGetMemFiler(b, 1e3) +} + +func benchmarkAllocatorRndGetSimpleFileFiler(b *testing.B, sz int) { + os.Remove(testDbName) + <-time.After(5 * time.Second) + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer func() { + f.Close() + os.Remove(testDbName) + }() + + benchmarkAllocatorRndGet(b, NewSimpleFileFiler(f), sz) +} + +func BenchmarkAllocatorRndGetSimpleFileFiler0(b *testing.B) { + benchmarkAllocatorRndGetSimpleFileFiler(b, 0) +} + +func BenchmarkAllocatorRndGetSimpleFileFiler1e1(b *testing.B) { + benchmarkAllocatorRndGetSimpleFileFiler(b, 1e1) +} + +func BenchmarkAllocatorRndGetSimpleFileFiler1e2(b *testing.B) { + benchmarkAllocatorRndGetSimpleFileFiler(b, 1e2) +} + +func BenchmarkAllocatorRndGetSimpleFileFiler1e3(b *testing.B) { + benchmarkAllocatorRndGetSimpleFileFiler(b, 1e3) +} + +func benchmarkAllocatorRndGetRollbackFiler(b *testing.B, sz int) { + os.Remove(testDbName) + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer func() { + f.Close() + os.Remove(testDbName) + }() + + g := NewSimpleFileFiler(f) + var filer *RollbackFiler + if filer, err = NewRollbackFiler( + g, + func(sz int64) error { + if err = g.Truncate(sz); err != nil { + return err + } + + return g.Sync() + }, + g, + ); err != nil { + b.Error(err) + return + } + + benchmarkAllocatorRndGet(b, filer, sz) +} + +func BenchmarkAllocatorRndGetRollbackFiler0(b *testing.B) { + benchmarkAllocatorRndGetRollbackFiler(b, 0) +} + +func BenchmarkAllocatorRndGetRollbackFiler1e1(b *testing.B) { + benchmarkAllocatorRndGetRollbackFiler(b, 1e1) +} + +func BenchmarkAllocatorRndGetRollbackFiler1e2(b *testing.B) { + benchmarkAllocatorRndGetRollbackFiler(b, 1e2) +} + +func BenchmarkAllocatorRndGetRollbackFiler1e3(b *testing.B) { + benchmarkAllocatorRndGetRollbackFiler(b, 1e3) +} + +func benchmarkAllocatorRndGetACIDFiler(b *testing.B, sz int) { + os.Remove(testDbName) + os.Remove(walName) + f, err := os.OpenFile(testDbName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer func() { + f.Close() + os.Remove(testDbName) + }() + + wal, err := os.OpenFile(walName, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600) + if err != nil { + b.Fatal(err) + } + + defer func() { + wal.Close() + os.Remove(walName) + }() + + filer, err := NewACIDFiler(NewSimpleFileFiler(f), wal) + if err != nil { + b.Error(err) + return + } + + benchmarkAllocatorRndGet(b, filer, sz) +} + +func BenchmarkAllocatorRndGetACIDFiler0(b *testing.B) { + benchmarkAllocatorRndGetACIDFiler(b, 0) +} + +func BenchmarkAllocatorRndGetACIDFiler1e1(b *testing.B) { + benchmarkAllocatorRndGetACIDFiler(b, 1e1) +} + +func BenchmarkAllocatorRndGetACIDFiler1e2(b *testing.B) { + benchmarkAllocatorRndGetACIDFiler(b, 1e2) +} + +func BenchmarkAllocatorRndGetACIDFiler1e3(b *testing.B) { + benchmarkAllocatorRndGetACIDFiler(b, 1e3) +} + +func TestFltFind(t *testing.T) { + var f flt + + f.init() + if h := f.find(1); h != 0 { + t.Fatal(h) + } + + // [0] + f.init() + f[0].head = 1 + if h := f.find(1); h != 1 || f[0].head != 0 { + t.Fatal(h) + } + + f.init() + f[0].head = 1 + if h := f.find(2); h != 0 || f[0].head == 0 { + t.Fatal(h) + } + + // [1] + f.init() + f[1].head = 1 + if h := f.find(1); h != 1 || f[1].head != 0 { + t.Fatal("\n", f, h) + } + + f.init() + f[1].head = 1 + if h := f.find(2); h != 1 || f[1].head != 0 { + t.Fatal(f, h) + } + + f.init() + f[1].head = 1 + if h := f.find(3); h != 0 || f[1].head == 0 { + t.Fatal(h) + } + + // [2] + f.init() + f[2].head = 1 + if h := f.find(1); h != 1 || f[2].head != 0 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.find(2); h != 1 || f[2].head != 0 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.find(3); h != 1 || f[2].head != 0 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.find(4); h != 1 || f[2].head != 0 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.find(5); h != 0 || f[2].head == 0 { + t.Fatal(h) + } +} + +func TestFltHead(t *testing.T) { + var f flt + f.init() + if h := f.head(1); h != 0 { + t.Fatal(h) + } + + // [0] + f.init() + f[0].head = 1 + if h := f.head(1); h != 1 { + t.Fatal(h) + } + + f.init() + f[0].head = 1 + if h := f.head(2); h != 0 { + t.Fatal(h) + } + + // [1] + f.init() + f[1].head = 1 + if h := f.head(1); h != 0 { + t.Fatal(h) + } + + f.init() + f[1].head = 1 + if h := f.head(2); h != 1 { + t.Fatal(h) + } + + f.init() + f[1].head = 1 + if h := f.head(3); h != 1 { + t.Fatal(h) + } + + f.init() + f[1].head = 1 + if h := f.head(4); h != 0 { + t.Fatal(h) + } + + // [2] + f.init() + f[2].head = 1 + if h := f.head(1); h != 0 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.head(2); h != 0 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.head(3); h != 0 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.head(4); h != 1 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.head(5); h != 1 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.head(6); h != 1 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.head(7); h != 1 { + t.Fatal(h) + } + + f.init() + f[2].head = 1 + if h := f.head(8); h != 0 { + t.Fatal(h) + } + +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/filer.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/filer.go new file mode 100644 index 000000000..38b389387 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/filer.go @@ -0,0 +1,192 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// An abstraction of file like (persistent) storage with optional (abstracted) +// support for structural integrity. + +package lldb + +import ( + "fmt" + + "github.com/cznic/mathutil" +) + +func doubleTrouble(first, second error) error { + return fmt.Errorf("%q. Additionally, while attempting to recover (rollback): %q", first, second) +} + +// A Filer is a []byte-like model of a file or similar entity. It may +// optionally implement support for structural transaction safety. In contrast +// to a file stream, a Filer is not sequentially accessible. ReadAt and WriteAt +// are always "addressed" by an offset and are assumed to perform atomically. +// A Filer is not safe for concurrent access, it's designed for consumption by +// the other objects in package, which should use a Filer from one goroutine +// only or via a mutex. BeginUpdate, EndUpdate and Rollback must be either all +// implemented by a Filer for structural integrity - or they should be all +// no-ops; where/if that requirement is relaxed. +// +// If a Filer wraps another Filer implementation, it usually invokes the same +// methods on the "inner" one, after some possible argument translations etc. +// If a Filer implements the structural transactions handling methods +// (BeginUpdate, EndUpdate and Rollback) as no-ops _and_ wraps another Filer: +// it then still MUST invoke those methods on the inner Filer. This is +// important for the case where a RollbackFiler exists somewhere down the +// chain. It's also important for an Allocator - to know when it must +// invalidate its FLT cache. +type Filer interface { + // BeginUpdate increments the "nesting" counter (initially zero). Every + // call to BeginUpdate must be eventually "balanced" by exactly one of + // EndUpdate or Rollback. Calls to BeginUpdate may nest. + BeginUpdate() error + + // Analogous to os.File.Close(). + Close() error + + // EndUpdate decrements the "nesting" counter. If it's zero after that + // then assume the "storage" has reached structural integrity (after a + // batch of partial updates). If a Filer implements some support for + // that (write ahead log, journal, etc.) then the appropriate actions + // are to be taken for nesting == 0. Invocation of an unbalanced + // EndUpdate is an error. + EndUpdate() error + + // Analogous to os.File.Name(). + Name() string + + // PunchHole deallocates space inside a "file" in the byte range + // starting at off and continuing for size bytes. The actual hole + // created by PunchHole may be smaller than requested. The Filer size + // (as reported by `Size()` does not change when hole punching, even + // when punching the end of a file off. In contrast to the Linux + // implementation of FALLOC_FL_PUNCH_HOLE in `fallocate`(2); a Filer is + // free not only to ignore `PunchHole()` (implement it as a nop), but + // additionally no guarantees about the content of the hole, when + // eventually read back, are required, i.e. any data, not only zeros, + // can be read from the "hole", including just anything what was left + // there - with all of the possible security problems. + PunchHole(off, size int64) error + + // As os.File.ReadAt. Note: `off` is an absolute "file pointer" + // address and cannot be negative even when a Filer is a InnerFiler. + ReadAt(b []byte, off int64) (n int, err error) + + // Rollback cancels and undoes the innermost pending update level. + // Rollback decrements the "nesting" counter. If a Filer implements + // some support for keeping structural integrity (write ahead log, + // journal, etc.) then the appropriate actions are to be taken. + // Invocation of an unbalanced Rollback is an error. + Rollback() error + + // Analogous to os.File.FileInfo().Size(). + Size() (int64, error) + + // Analogous to os.Sync(). + Sync() (err error) + + // Analogous to os.File.Truncate(). + Truncate(size int64) error + + // Analogous to os.File.WriteAt(). Note: `off` is an absolute "file + // pointer" address and cannot be negative even when a Filer is a + // InnerFiler. + WriteAt(b []byte, off int64) (n int, err error) +} + +var _ Filer = &InnerFiler{} // Ensure InnerFiler is a Filer. + +// A InnerFiler is a Filer with added addressing/size translation. +type InnerFiler struct { + outer Filer + off int64 +} + +// NewInnerFiler returns a new InnerFiler wrapped by `outer` in a way which +// adds `off` to every access. +// +// For example, considering: +// +// inner := NewInnerFiler(outer, 10) +// +// then +// +// inner.WriteAt([]byte{42}, 4) +// +// translates to +// +// outer.WriteAt([]byte{42}, 14) +// +// But an attempt to emulate +// +// outer.WriteAt([]byte{17}, 9) +// +// by +// +// inner.WriteAt([]byte{17}, -1) +// +// will fail as the `off` parameter can never be < 0. Also note that +// +// inner.Size() == outer.Size() - off, +// +// i.e. `inner` pretends no `outer` exists. Finally, after e.g. +// +// inner.Truncate(7) +// outer.Size() == 17 +// +// will be true. +func NewInnerFiler(outer Filer, off int64) *InnerFiler { return &InnerFiler{outer, off} } + +// BeginUpdate implements Filer. +func (f *InnerFiler) BeginUpdate() error { return f.outer.BeginUpdate() } + +// Close implements Filer. +func (f *InnerFiler) Close() (err error) { return f.outer.Close() } + +// EndUpdate implements Filer. +func (f *InnerFiler) EndUpdate() error { return f.outer.EndUpdate() } + +// Name implements Filer. +func (f *InnerFiler) Name() string { return f.outer.Name() } + +// PunchHole implements Filer. `off`, `size` must be >= 0. +func (f *InnerFiler) PunchHole(off, size int64) error { return f.outer.PunchHole(f.off+off, size) } + +// ReadAt implements Filer. `off` must be >= 0. +func (f *InnerFiler) ReadAt(b []byte, off int64) (n int, err error) { + if off < 0 { + return 0, &ErrINVAL{f.outer.Name() + ":ReadAt invalid off", off} + } + + return f.outer.ReadAt(b, f.off+off) +} + +// Rollback implements Filer. +func (f *InnerFiler) Rollback() error { return f.outer.Rollback() } + +// Size implements Filer. +func (f *InnerFiler) Size() (int64, error) { + sz, err := f.outer.Size() + if err != nil { + return 0, err + } + + return mathutil.MaxInt64(sz-f.off, 0), nil +} + +// Sync() implements Filer. +func (f *InnerFiler) Sync() (err error) { + return f.outer.Sync() +} + +// Truncate implements Filer. +func (f *InnerFiler) Truncate(size int64) error { return f.outer.Truncate(size + f.off) } + +// WriteAt implements Filer. `off` must be >= 0. +func (f *InnerFiler) WriteAt(b []byte, off int64) (n int, err error) { + if off < 0 { + return 0, &ErrINVAL{f.outer.Name() + ":WriteAt invalid off", off} + } + + return f.outer.WriteAt(b, f.off+off) +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/filer_test.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/filer_test.go new file mode 100644 index 000000000..e2da7a187 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/filer_test.go @@ -0,0 +1,764 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lldb + +import ( + "bytes" + "encoding/hex" + "io/ioutil" + "math/rand" + "os" + "runtime" + "testing" + + "github.com/cznic/fileutil" +) + +// Bench knobs. +const ( + filerTestChunkSize = 32e3 + filerTotalSize = 10e6 +) + +type newFunc func() Filer + +type testFileFiler struct { + Filer +} + +func (t *testFileFiler) Close() (err error) { + n := t.Name() + err = t.Filer.Close() + if errDel := os.Remove(n); errDel != nil && err == nil { + err = errDel + } + return +} + +var ( + newFileFiler = func() Filer { + file, err := ioutil.TempFile("", "lldb-test-file") + if err != nil { + panic(err) + } + + return &testFileFiler{NewSimpleFileFiler(file)} + } + + newOSFileFiler = func() Filer { + file, err := ioutil.TempFile("", "lldb-test-osfile") + if err != nil { + panic(err) + } + + return &testFileFiler{NewOSFiler(file)} + } + + newMemFiler = func() Filer { + return NewMemFiler() + } + + nwBitFiler = func() Filer { + f, err := newBitFiler(NewMemFiler()) + if err != nil { + panic(err) + } + + return f + } + + newRollbackFiler = func() Filer { + f := NewMemFiler() + + var r Filer + + checkpoint := func(sz int64) (err error) { + return f.Truncate(sz) + } + + r, err := NewRollbackFiler(f, checkpoint, f) + if err != nil { + panic(err) + } + + return r + } +) + +func TestFilerNesting(t *testing.T) { + testFilerNesting(t, newFileFiler) + testFilerNesting(t, newOSFileFiler) + testFilerNesting(t, newMemFiler) + testFilerNesting(t, newRollbackFiler) +} + +func testFilerNesting(t *testing.T, nf newFunc) { + // Check {Create, Close} works. + f := nf() + t.Log(f.Name()) + if err := f.Close(); err != nil { + t.Fatal(err) + } + + // Check {Create, EndUpdate} doesn't work. + f = nf() + t.Log(f.Name()) + if err := f.EndUpdate(); err == nil { + f.Close() + t.Fatal("unexpected success") + } + + if err := f.Close(); err != nil { + t.Fatal(err) + } + + // Check {Create, BeginUpdate, Close} doesn't work. + f = nf() + t.Log(f.Name()) + f.BeginUpdate() + + if err := f.Close(); err == nil { + t.Fatal("unexpected success") + } + + // Check {Create, BeginUpdate, EndUpdate, Close} works. + f = nf() + t.Log(f.Name()) + f.BeginUpdate() + if err := f.EndUpdate(); err != nil { + f.Close() + t.Fatal(err) + } + + if err := f.Close(); err != nil { + t.Fatal(err) + } +} + +func TestFilerTruncate(t *testing.T) { + testFilerTruncate(t, newFileFiler) + testFilerTruncate(t, newOSFileFiler) + testFilerTruncate(t, newMemFiler) + testFilerTruncate(t, nwBitFiler) + testFilerTruncate(t, newRollbackFiler) +} + +func testFilerTruncate(t *testing.T, nf newFunc) { + f := nf() + t.Log(f.Name()) + defer func() { + if err := f.Close(); err != nil { + t.Error(err) + } + }() + + if _, ok := f.(*RollbackFiler); ok { + if err := f.BeginUpdate(); err != nil { + t.Fatal(err) + } + + defer func() { + if err := f.EndUpdate(); err != nil { + t.Error(err) + } + }() + } + + // Check Truncate works. + sz := int64(1e6) + if err := f.Truncate(sz); err != nil { + t.Error(err) + return + } + + fsz, err := f.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := fsz, sz; g != e { + t.Error(g, e) + return + } + + sz *= 2 + if err := f.Truncate(sz); err != nil { + t.Error(err) + return + } + + fsz, err = f.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := fsz, sz; g != e { + t.Error(g, e) + return + } + + sz = 0 + if err := f.Truncate(sz); err != nil { + t.Error(err) + return + } + + fsz, err = f.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := fsz, sz; g != e { + t.Error(g, e) + return + } + + // Check Truncate(-1) doesn't work. + sz = -1 + if err := f.Truncate(sz); err == nil { + t.Error(err) + return + } + +} + +func TestFilerReadAtWriteAt(t *testing.T) { + testFilerReadAtWriteAt(t, newFileFiler) + testFilerReadAtWriteAt(t, newOSFileFiler) + testFilerReadAtWriteAt(t, newMemFiler) + testFilerReadAtWriteAt(t, nwBitFiler) + testFilerReadAtWriteAt(t, newRollbackFiler) +} + +func testFilerReadAtWriteAt(t *testing.T, nf newFunc) { + f := nf() + t.Log(f.Name()) + defer func() { + if err := f.Close(); err != nil { + t.Error(err) + } + }() + + if _, ok := f.(*RollbackFiler); ok { + if err := f.BeginUpdate(); err != nil { + t.Fatal(err) + } + + defer func() { + if err := f.EndUpdate(); err != nil { + t.Error(err) + } + }() + } + + const ( + N = 1 << 16 + M = 2e2 + ) + + s := make([]byte, N) + e := make([]byte, N) + rnd := rand.New(rand.NewSource(42)) + for i := range e { + s[i] = byte(rnd.Intn(256)) + } + n2 := 0 + for i := 0; i < M; i++ { + var from, to int + for { + from = rnd.Intn(N) + to = rnd.Intn(N) + if from != to { + break + } + } + if from > to { + from, to = to, from + } + for i := range s[from:to] { + s[from+i] = byte(rnd.Intn(256)) + } + copy(e[from:to], s[from:to]) + if to > n2 { + n2 = to + } + n, err := f.WriteAt(s[from:to], int64(from)) + if err != nil { + t.Error(err) + return + } + + if g, e := n, to-from; g != e { + t.Error(g, e) + return + } + } + + fsz, err := f.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := fsz, int64(n2); g != e { + t.Error(g, e) + return + } + + b := make([]byte, n2) + for i := 0; i <= M; i++ { + from := rnd.Intn(n2) + to := rnd.Intn(n2) + if from > to { + from, to = to, from + } + if i == M { + from, to = 0, n2 + } + n, err := f.ReadAt(b[from:to], int64(from)) + if err != nil && (!fileutil.IsEOF(err) && n != 0) { + fsz, err = f.Size() + if err != nil { + t.Error(err) + return + } + + t.Error(fsz, from, to, err) + return + } + + if g, e := n, to-from; g != e { + t.Error(g, e) + return + } + + if g, e := b[from:to], e[from:to]; !bytes.Equal(g, e) { + if x, ok := f.(*MemFiler); ok { + for i := int64(0); i <= 3; i++ { + t.Logf("pg %d\n----\n%s", i, hex.Dump(x.m[i][:])) + } + } + t.Errorf( + "i %d from %d to %d len(g) %d len(e) %d\n---- got ----\n%s\n---- exp ----\n%s", + i, from, to, len(g), len(e), hex.Dump(g), hex.Dump(e), + ) + return + } + } + + mf, ok := f.(*MemFiler) + if !ok { + return + } + + buf := &bytes.Buffer{} + if _, err := mf.WriteTo(buf); err != nil { + t.Error(err) + return + } + + if g, e := buf.Bytes(), e[:n2]; !bytes.Equal(g, e) { + t.Errorf("\nlen %d\n%s\nlen %d\n%s", len(g), hex.Dump(g), len(e), hex.Dump(e)) + return + } + + if err := mf.Truncate(0); err != nil { + t.Error(err) + return + } + + if _, err := mf.ReadFrom(buf); err != nil { + t.Error(err) + return + } + + roundTrip := make([]byte, n2) + if n, err := mf.ReadAt(roundTrip, 0); err != nil && n == 0 { + t.Error(err) + return + } + + if g, e := roundTrip, e[:n2]; !bytes.Equal(g, e) { + t.Errorf("\nlen %d\n%s\nlen %d\n%s", len(g), hex.Dump(g), len(e), hex.Dump(e)) + return + } +} + +func TestInnerFiler(t *testing.T) { + testInnerFiler(t, newFileFiler) + testInnerFiler(t, newOSFileFiler) + testInnerFiler(t, newMemFiler) + testInnerFiler(t, nwBitFiler) + testInnerFiler(t, newRollbackFiler) +} + +func testInnerFiler(t *testing.T, nf newFunc) { + const ( + HDR_SIZE = 42 + LONG_OFF = 3330 + ) + outer := nf() + t.Log(outer.Name()) + inner := NewInnerFiler(outer, HDR_SIZE) + defer func() { + if err := outer.Close(); err != nil { + t.Error(err) + } + }() + + if _, ok := outer.(*RollbackFiler); ok { + if err := outer.BeginUpdate(); err != nil { + t.Fatal(err) + } + + defer func() { + if err := outer.EndUpdate(); err != nil { + t.Error(err) + } + }() + } + + b := []byte{2, 5, 11} + n, err := inner.WriteAt(b, -1) + if err == nil { + t.Error("unexpected success") + return + } + + n, err = inner.ReadAt(make([]byte, 10), -1) + if err == nil { + t.Error("unexpected success") + return + } + + n, err = inner.WriteAt(b, 0) + if err != nil { + t.Error(err) + return + } + + if g, e := n, len(b); g != e { + t.Error(g, e) + return + } + + osz, err := outer.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := osz, int64(HDR_SIZE+3); g != e { + t.Error(g, e) + return + } + + isz, err := inner.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := isz, int64(3); g != e { + t.Error(g, e) + return + } + + rbuf := make([]byte, 3) + if n, err = outer.ReadAt(rbuf, 0); err != nil && n == 0 { + t.Error(err) + return + } + + if g, e := n, len(rbuf); g != e { + t.Error(g, e) + return + } + + if g, e := rbuf, make([]byte, 3); !bytes.Equal(g, e) { + t.Error(g, e) + } + + rbuf = make([]byte, 3) + if n, err = outer.ReadAt(rbuf, HDR_SIZE); err != nil && n == 0 { + t.Error(err) + return + } + + if g, e := n, len(rbuf); g != e { + t.Error(g, e) + return + } + + if g, e := rbuf, []byte{2, 5, 11}; !bytes.Equal(g, e) { + t.Error(g, e) + } + + rbuf = make([]byte, 3) + if n, err = inner.ReadAt(rbuf, 0); err != nil && n == 0 { + t.Error(err) + return + } + + if g, e := n, len(rbuf); g != e { + t.Error(g, e) + return + } + + if g, e := rbuf, []byte{2, 5, 11}; !bytes.Equal(g, e) { + t.Error(g, e) + } + + b = []byte{22, 55, 111} + if n, err = inner.WriteAt(b, LONG_OFF); err != nil { + t.Error(err) + return + } + + if g, e := n, len(b); g != e { + t.Error(g, e) + return + } + + osz, err = outer.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := osz, int64(HDR_SIZE+LONG_OFF+3); g != e { + t.Error(g, e) + return + } + + isz, err = inner.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := isz, int64(LONG_OFF+3); g != e { + t.Error(g, e) + return + } + + rbuf = make([]byte, 3) + if n, err = outer.ReadAt(rbuf, HDR_SIZE+LONG_OFF); err != nil && n == 0 { + t.Error(err) + return + } + + if g, e := n, len(rbuf); g != e { + t.Error(g, e) + return + } + + if g, e := rbuf, []byte{22, 55, 111}; !bytes.Equal(g, e) { + t.Error(g, e) + } + + rbuf = make([]byte, 3) + if n, err = inner.ReadAt(rbuf, LONG_OFF); err != nil && n == 0 { + t.Error(err) + return + } + + if g, e := n, len(rbuf); g != e { + t.Error(g, e) + return + } + + if g, e := rbuf, []byte{22, 55, 111}; !bytes.Equal(g, e) { + t.Error(g, e) + return + } + + if err = inner.Truncate(1); err != nil { + t.Error(err) + return + } + + isz, err = inner.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := isz, int64(1); g != e { + t.Error(g, e) + return + } + + osz, err = outer.Size() + if err != nil { + t.Error(err) + return + } + + if g, e := osz, int64(HDR_SIZE+1); g != e { + t.Error(g, e) + return + } +} + +func TestFileReadAtHole(t *testing.T) { + testFileReadAtHole(t, newFileFiler) + testFileReadAtHole(t, newOSFileFiler) + testFileReadAtHole(t, newMemFiler) + testFileReadAtHole(t, nwBitFiler) + testFileReadAtHole(t, newRollbackFiler) +} + +func testFileReadAtHole(t *testing.T, nf newFunc) { + f := nf() + t.Log(f.Name()) + defer func() { + if err := f.Close(); err != nil { + t.Error(err) + } + }() + + if _, ok := f.(*RollbackFiler); ok { + if err := f.BeginUpdate(); err != nil { + t.Fatal(err) + } + + defer func() { + if err := f.EndUpdate(); err != nil { + t.Error(err) + } + }() + } + + n, err := f.WriteAt([]byte{1}, 40000) + if err != nil { + t.Error(err) + return + } + + if n != 1 { + t.Error(n) + return + } + + n, err = f.ReadAt(make([]byte, 1000), 20000) + if err != nil { + t.Error(err) + return + } + + if n != 1000 { + t.Error(n) + return + } +} + +func BenchmarkMemFilerWrSeq(b *testing.B) { + b.StopTimer() + buf := make([]byte, filerTestChunkSize) + for i := range buf { + buf[i] = byte(rand.Int()) + } + f := newMemFiler() + runtime.GC() + b.StartTimer() + var ofs int64 + for i := 0; i < b.N; i++ { + _, err := f.WriteAt(buf, ofs) + if err != nil { + b.Fatal(err) + } + + ofs = (ofs + filerTestChunkSize) % filerTotalSize + } +} + +func BenchmarkMemFilerRdSeq(b *testing.B) { + b.StopTimer() + buf := make([]byte, filerTestChunkSize) + for i := range buf { + buf[i] = byte(rand.Int()) + } + f := newMemFiler() + var ofs int64 + for i := 0; i < b.N; i++ { + _, err := f.WriteAt(buf, ofs) + if err != nil { + b.Fatal(err) + } + + ofs = (ofs + filerTestChunkSize) % filerTotalSize + } + runtime.GC() + b.StartTimer() + ofs = 0 + for i := 0; i < b.N; i++ { + n, err := f.ReadAt(buf, ofs) + if err != nil && n == 0 { + b.Fatal(err) + } + + ofs = (ofs + filerTestChunkSize) % filerTotalSize + } +} + +func BenchmarkMemFilerWrRand(b *testing.B) { + b.StopTimer() + rng := rand.New(rand.NewSource(42)) + f := newMemFiler() + var bytes int64 + + var ofs, runs []int + for i := 0; i < b.N; i++ { + ofs = append(ofs, rng.Intn(1<<31-1)) + runs = append(runs, rng.Intn(1<<31-1)%(2*pgSize)) + } + data := make([]byte, 2*pgSize) + for i := range data { + data[i] = byte(rng.Int()) + } + + runtime.GC() + b.StartTimer() + for i, v := range ofs { + n := runs[i] + bytes += int64(n) + f.WriteAt(data[:n], int64(v)) + } + b.StopTimer() +} + +func BenchmarkMemFilerRdRand(b *testing.B) { + b.StopTimer() + rng := rand.New(rand.NewSource(42)) + f := newMemFiler() + var bytes int64 + + var ofs, runs []int + for i := 0; i < b.N; i++ { + ofs = append(ofs, rng.Intn(1<<31-1)) + runs = append(runs, rng.Intn(1<<31-1)%(2*pgSize)) + } + data := make([]byte, 2*pgSize) + for i := range data { + data[i] = byte(rng.Int()) + } + + for i, v := range ofs { + n := runs[i] + bytes += int64(n) + f.WriteAt(data[:n], int64(v)) + } + + runtime.GC() + b.StartTimer() + for _, v := range ofs { + f.ReadAt(data, int64(v)) + } + b.StopTimer() +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/gb.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/gb.go new file mode 100644 index 000000000..e9090a547 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/gb.go @@ -0,0 +1,812 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Utilities to encode/decode and collate Go predeclared scalar types (and the +// typeless nil and []byte). The encoding format is a variation of the one +// used by the "encoding/gob" package. + +package lldb + +import ( + "bytes" + "fmt" + "math" + + "github.com/cznic/mathutil" +) + +const ( + gbNull = iota // 0x00 + gbFalse // 0x01 + gbTrue // 0x02 + gbFloat0 // 0x03 + gbFloat1 // 0x04 + gbFloat2 // 0x05 + gbFloat3 // 0x06 + gbFloat4 // 0x07 + gbFloat5 // 0x08 + gbFloat6 // 0x09 + gbFloat7 // 0x0a + gbFloat8 // 0x0b + gbComplex0 // 0x0c + gbComplex1 // 0x0d + gbComplex2 // 0x0e + gbComplex3 // 0x0f + gbComplex4 // 0x10 + gbComplex5 // 0x11 + gbComplex6 // 0x12 + gbComplex7 // 0x13 + gbComplex8 // 0x14 + gbBytes00 // 0x15 + gbBytes01 // 0x16 + gbBytes02 // 0x17 + gbBytes03 // 0x18 + gbBytes04 // 0x19 + gbBytes05 // 0x1a + gbBytes06 // 0x1b + gbBytes07 // 0x1c + gbBytes08 // 0x1d + gbBytes09 // 0x1e + gbBytes10 // 0x1f + gbBytes11 // 0x20 + gbBytes12 // 0x21 + gbBytes13 // 0x22 + gbBytes14 // 0x23 + gbBytes15 // 0x24 + gbBytes16 // 0x25 + gbBytes17 // Ox26 + gbBytes1 // 0x27 + gbBytes2 // 0x28: Offset by one to allow 64kB sized []byte. + gbString00 // 0x29 + gbString01 // 0x2a + gbString02 // 0x2b + gbString03 // 0x2c + gbString04 // 0x2d + gbString05 // 0x2e + gbString06 // 0x2f + gbString07 // 0x30 + gbString08 // 0x31 + gbString09 // 0x32 + gbString10 // 0x33 + gbString11 // 0x34 + gbString12 // 0x35 + gbString13 // 0x36 + gbString14 // 0x37 + gbString15 // 0x38 + gbString16 // 0x39 + gbString17 // 0x3a + gbString1 // 0x3b + gbString2 // 0x3c + gbUintP1 // 0x3d + gbUintP2 // 0x3e + gbUintP3 // 0x3f + gbUintP4 // 0x40 + gbUintP5 // 0x41 + gbUintP6 // 0x42 + gbUintP7 // 0x43 + gbUintP8 // 0x44 + gbIntM8 // 0x45 + gbIntM7 // 0x46 + gbIntM6 // 0x47 + gbIntM5 // 0x48 + gbIntM4 // 0x49 + gbIntM3 // 0x4a + gbIntM2 // 0x4b + gbIntM1 // 0x4c + gbIntP1 // 0x4d + gbIntP2 // 0x4e + gbIntP3 // 0x4f + gbIntP4 // 0x50 + gbIntP5 // 0x51 + gbIntP6 // 0x52 + gbIntP7 // 0x53 + gbIntP8 // 0x54 + gbInt0 // 0x55 + + gbIntMax = 255 - gbInt0 // 0xff == 170 +) + +// EncodeScalars encodes a vector of predeclared scalar type values to a +// []byte, making it suitable to store it as a "record" in a DB or to use it as +// a key of a BTree. +func EncodeScalars(scalars ...interface{}) (b []byte, err error) { + for _, scalar := range scalars { + switch x := scalar.(type) { + default: + return nil, &ErrINVAL{"EncodeScalars: unsupported type", fmt.Sprintf("%T in `%#v`", x, scalars)} + + case nil: + b = append(b, gbNull) + + case bool: + switch x { + case false: + b = append(b, gbFalse) + case true: + b = append(b, gbTrue) + } + + case float32: + encFloat(float64(x), &b) + case float64: + encFloat(x, &b) + + case complex64: + encComplex(complex128(x), &b) + case complex128: + encComplex(x, &b) + + case string: + n := len(x) + if n <= 17 { + b = append(b, byte(gbString00+n)) + b = append(b, []byte(x)...) + break + } + + if n > 65535 { + return nil, fmt.Errorf("EncodeScalars: cannot encode string of length %d (limit 65536)", n) + } + + pref := byte(gbString1) + if n > 255 { + pref++ + } + b = append(b, pref) + encUint0(uint64(n), &b) + b = append(b, []byte(x)...) + + case int8: + encInt(int64(x), &b) + case int16: + encInt(int64(x), &b) + case int32: + encInt(int64(x), &b) + case int64: + encInt(x, &b) + case int: + encInt(int64(x), &b) + + case uint8: + encUint(uint64(x), &b) + case uint16: + encUint(uint64(x), &b) + case uint32: + encUint(uint64(x), &b) + case uint64: + encUint(x, &b) + case uint: + encUint(uint64(x), &b) + case []byte: + n := len(x) + if n <= 17 { + b = append(b, byte(gbBytes00+n)) + b = append(b, []byte(x)...) + break + } + + if n > 655356 { + return nil, fmt.Errorf("EncodeScalars: cannot encode []byte of length %d (limit 65536)", n) + } + + pref := byte(gbBytes1) + if n > 255 { + pref++ + } + b = append(b, pref) + if n <= 255 { + b = append(b, byte(n)) + } else { + n-- + b = append(b, byte(n>>8), byte(n)) + } + b = append(b, x...) + } + } + return +} + +func encComplex(f complex128, b *[]byte) { + encFloatPrefix(gbComplex0, real(f), b) + encFloatPrefix(gbComplex0, imag(f), b) +} + +func encFloatPrefix(prefix byte, f float64, b *[]byte) { + u := math.Float64bits(f) + var n uint64 + for i := 0; i < 8; i++ { + n <<= 8 + n |= u & 0xFF + u >>= 8 + } + bits := mathutil.BitLenUint64(n) + if bits == 0 { + *b = append(*b, prefix) + return + } + + // 0 1 2 3 4 5 6 7 8 9 + // . 1 1 1 1 1 1 1 1 2 + encUintPrefix(prefix+1+byte((bits-1)>>3), n, b) +} + +func encFloat(f float64, b *[]byte) { + encFloatPrefix(gbFloat0, f, b) +} + +func encUint0(n uint64, b *[]byte) { + switch { + case n <= 0xff: + *b = append(*b, byte(n)) + case n <= 0xffff: + *b = append(*b, byte(n>>8), byte(n)) + case n <= 0xffffff: + *b = append(*b, byte(n>>16), byte(n>>8), byte(n)) + case n <= 0xffffffff: + *b = append(*b, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n <= 0xffffffffff: + *b = append(*b, byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n <= 0xffffffffffff: + *b = append(*b, byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n <= 0xffffffffffffff: + *b = append(*b, byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n <= math.MaxUint64: + *b = append(*b, byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + } +} + +func encUintPrefix(prefix byte, n uint64, b *[]byte) { + *b = append(*b, prefix) + encUint0(n, b) +} + +func encUint(n uint64, b *[]byte) { + bits := mathutil.Max(1, mathutil.BitLenUint64(n)) + encUintPrefix(gbUintP1+byte((bits-1)>>3), n, b) +} + +func encInt(n int64, b *[]byte) { + switch { + case n < -0x100000000000000: + *b = append(*b, byte(gbIntM8), byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n < -0x1000000000000: + *b = append(*b, byte(gbIntM7), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n < -0x10000000000: + *b = append(*b, byte(gbIntM6), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n < -0x100000000: + *b = append(*b, byte(gbIntM5), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n < -0x1000000: + *b = append(*b, byte(gbIntM4), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n < -0x10000: + *b = append(*b, byte(gbIntM3), byte(n>>16), byte(n>>8), byte(n)) + case n < -0x100: + *b = append(*b, byte(gbIntM2), byte(n>>8), byte(n)) + case n < 0: + *b = append(*b, byte(gbIntM1), byte(n)) + case n <= gbIntMax: + *b = append(*b, byte(gbInt0+n)) + case n <= 0xff: + *b = append(*b, gbIntP1, byte(n)) + case n <= 0xffff: + *b = append(*b, gbIntP2, byte(n>>8), byte(n)) + case n <= 0xffffff: + *b = append(*b, gbIntP3, byte(n>>16), byte(n>>8), byte(n)) + case n <= 0xffffffff: + *b = append(*b, gbIntP4, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n <= 0xffffffffff: + *b = append(*b, gbIntP5, byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n <= 0xffffffffffff: + *b = append(*b, gbIntP6, byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n <= 0xffffffffffffff: + *b = append(*b, gbIntP7, byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + case n <= 0x7fffffffffffffff: + *b = append(*b, gbIntP8, byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + } +} + +func decodeFloat(b []byte) float64 { + var u uint64 + for i, v := range b { + u |= uint64(v) << uint((i+8-len(b))*8) + } + return math.Float64frombits(u) +} + +// DecodeScalars decodes a []byte produced by EncodeScalars. +func DecodeScalars(b []byte) (scalars []interface{}, err error) { + b0 := b + for len(b) != 0 { + switch tag := b[0]; tag { + //default: + //return nil, fmt.Errorf("tag %d(%#x) not supported", b[0], b[0]) + case gbNull: + scalars = append(scalars, nil) + b = b[1:] + case gbFalse: + scalars = append(scalars, false) + b = b[1:] + case gbTrue: + scalars = append(scalars, true) + b = b[1:] + case gbFloat0: + scalars = append(scalars, 0.0) + b = b[1:] + case gbFloat1, gbFloat2, gbFloat3, gbFloat4, gbFloat5, gbFloat6, gbFloat7, gbFloat8: + n := 1 + int(tag) - gbFloat0 + if len(b) < n-1 { + goto corrupted + } + + scalars = append(scalars, decodeFloat(b[1:n])) + b = b[n:] + case gbComplex0, gbComplex1, gbComplex2, gbComplex3, gbComplex4, gbComplex5, gbComplex6, gbComplex7, gbComplex8: + n := 1 + int(tag) - gbComplex0 + if len(b) < n-1 { + goto corrupted + } + + re := decodeFloat(b[1:n]) + b = b[n:] + + if len(b) == 0 { + goto corrupted + } + + tag = b[0] + if tag < gbComplex0 || tag > gbComplex8 { + goto corrupted + } + + n = 1 + int(tag) - gbComplex0 + if len(b) < n-1 { + goto corrupted + } + + scalars = append(scalars, complex(re, decodeFloat(b[1:n]))) + b = b[n:] + case gbBytes00, gbBytes01, gbBytes02, gbBytes03, gbBytes04, + gbBytes05, gbBytes06, gbBytes07, gbBytes08, gbBytes09, + gbBytes10, gbBytes11, gbBytes12, gbBytes13, gbBytes14, + gbBytes15, gbBytes16, gbBytes17: + n := int(tag - gbBytes00) + if len(b) < n+1 { + goto corrupted + } + + scalars = append(scalars, append([]byte(nil), b[1:n+1]...)) + b = b[n+1:] + case gbBytes1: + if len(b) < 2 { + goto corrupted + } + + n := int(b[1]) + b = b[2:] + if len(b) < n { + goto corrupted + } + + scalars = append(scalars, append([]byte(nil), b[:n]...)) + b = b[n:] + case gbBytes2: + if len(b) < 3 { + goto corrupted + } + + n := int(b[1])<<8 | int(b[2]) + 1 + b = b[3:] + if len(b) < n { + goto corrupted + } + + scalars = append(scalars, append([]byte(nil), b[:n]...)) + b = b[n:] + case gbString00, gbString01, gbString02, gbString03, gbString04, + gbString05, gbString06, gbString07, gbString08, gbString09, + gbString10, gbString11, gbString12, gbString13, gbString14, + gbString15, gbString16, gbString17: + n := int(tag - gbString00) + if len(b) < n+1 { + goto corrupted + } + + scalars = append(scalars, string(b[1:n+1])) + b = b[n+1:] + case gbString1: + if len(b) < 2 { + goto corrupted + } + + n := int(b[1]) + b = b[2:] + if len(b) < n { + goto corrupted + } + + scalars = append(scalars, string(b[:n])) + b = b[n:] + case gbString2: + if len(b) < 3 { + goto corrupted + } + + n := int(b[1])<<8 | int(b[2]) + b = b[3:] + if len(b) < n { + goto corrupted + } + + scalars = append(scalars, string(b[:n])) + b = b[n:] + case gbUintP1, gbUintP2, gbUintP3, gbUintP4, gbUintP5, gbUintP6, gbUintP7, gbUintP8: + b = b[1:] + n := 1 + int(tag) - gbUintP1 + if len(b) < n { + goto corrupted + } + + var u uint64 + for _, v := range b[:n] { + u = u<<8 | uint64(v) + } + scalars = append(scalars, u) + b = b[n:] + case gbIntM8, gbIntM7, gbIntM6, gbIntM5, gbIntM4, gbIntM3, gbIntM2, gbIntM1: + b = b[1:] + n := 8 - (int(tag) - gbIntM8) + if len(b) < n { + goto corrupted + } + u := uint64(math.MaxUint64) + for _, v := range b[:n] { + u = u<<8 | uint64(v) + } + scalars = append(scalars, int64(u)) + b = b[n:] + case gbIntP1, gbIntP2, gbIntP3, gbIntP4, gbIntP5, gbIntP6, gbIntP7, gbIntP8: + b = b[1:] + n := 1 + int(tag) - gbIntP1 + if len(b) < n { + goto corrupted + } + + i := int64(0) + for _, v := range b[:n] { + i = i<<8 | int64(v) + } + scalars = append(scalars, i) + b = b[n:] + default: + scalars = append(scalars, int64(b[0])-gbInt0) + b = b[1:] + } + } + return append([]interface{}(nil), scalars...), nil + +corrupted: + return nil, &ErrDecodeScalars{append([]byte(nil), b0...), len(b0) - len(b)} +} + +func collateComplex(x, y complex128) int { + switch rx, ry := real(x), real(y); { + case rx < ry: + return -1 + case rx == ry: + switch ix, iy := imag(x), imag(y); { + case ix < iy: + return -1 + case ix == iy: + return 0 + case ix > iy: + return 1 + } + } + //case rx > ry: + return 1 +} + +func collateFloat(x, y float64) int { + switch { + case x < y: + return -1 + case x == y: + return 0 + } + //case x > y: + return 1 +} + +func collateInt(x, y int64) int { + switch { + case x < y: + return -1 + case x == y: + return 0 + } + //case x > y: + return 1 +} + +func collateUint(x, y uint64) int { + switch { + case x < y: + return -1 + case x == y: + return 0 + } + //case x > y: + return 1 +} + +func collateIntUint(x int64, y uint64) int { + if y > math.MaxInt64 { + return -1 + } + + return collateInt(x, int64(y)) +} + +func collateUintInt(x uint64, y int64) int { + return -collateIntUint(y, x) +} + +func collateType(i interface{}) (r interface{}, err error) { + switch x := i.(type) { + default: + return nil, fmt.Errorf("invalid collate type %T", x) + case nil: + return i, nil + case bool: + return i, nil + case int8: + return int64(x), nil + case int16: + return int64(x), nil + case int32: + return int64(x), nil + case int64: + return i, nil + case int: + return int64(x), nil + case uint8: + return uint64(x), nil + case uint16: + return uint64(x), nil + case uint32: + return uint64(x), nil + case uint64: + return i, nil + case uint: + return uint64(x), nil + case float32: + return float64(x), nil + case float64: + return i, nil + case complex64: + return complex128(x), nil + case complex128: + return i, nil + case []byte: + return i, nil + case string: + return i, nil + } +} + +// Collate collates two arrays of Go predeclared scalar types (and the typeless +// nil or []byte). If any other type appears in x or y, Collate will return a +// non nil error. String items are collated using strCollate or lexically +// byte-wise (as when using Go comparison operators) when strCollate is nil. +// []byte items are collated using bytes.Compare. +// +// Collate returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +// The same value as defined above must be returned from strCollate. +// +// The "outer" ordering is: nil, bool, number, []byte, string. IOW, nil is +// "smaller" than anything else except other nil, numbers collate before +// []byte, []byte collate before strings, etc. +// +// Integers and real numbers collate as expected in math. However, complex +// numbers are not ordered in Go. Here the ordering is defined: Complex numbers +// are in comparison considered first only by their real part. Iff the result +// is equality then the imaginary part is used to determine the ordering. In +// this "second order" comparing, integers and real numbers are considered as +// complex numbers with a zero imaginary part. +func Collate(x, y []interface{}, strCollate func(string, string) int) (r int, err error) { + nx, ny := len(x), len(y) + + switch { + case nx == 0 && ny != 0: + return -1, nil + case nx == 0 && ny == 0: + return 0, nil + case nx != 0 && ny == 0: + return 1, nil + } + + r = 1 + if nx > ny { + x, y, r = y, x, -r + } + + var c int + for i, xi0 := range x { + yi0 := y[i] + xi, err := collateType(xi0) + if err != nil { + return 0, err + } + + yi, err := collateType(yi0) + if err != nil { + return 0, err + } + + switch x := xi.(type) { + default: + panic(fmt.Errorf("internal error: %T", x)) + + case nil: + switch yi.(type) { + case nil: + // nop + default: + return -r, nil + } + + case bool: + switch y := yi.(type) { + case nil: + return r, nil + case bool: + switch { + case !x && y: + return -r, nil + case x == y: + // nop + case x && !y: + return r, nil + } + default: + return -r, nil + } + + case int64: + switch y := yi.(type) { + case nil, bool: + return r, nil + case int64: + c = collateInt(x, y) + case uint64: + c = collateIntUint(x, y) + case float64: + c = collateFloat(float64(x), y) + case complex128: + c = collateComplex(complex(float64(x), 0), y) + case []byte: + return -r, nil + case string: + return -r, nil + } + + if c != 0 { + return c * r, nil + } + + case uint64: + switch y := yi.(type) { + case nil, bool: + return r, nil + case int64: + c = collateUintInt(x, y) + case uint64: + c = collateUint(x, y) + case float64: + c = collateFloat(float64(x), y) + case complex128: + c = collateComplex(complex(float64(x), 0), y) + case []byte: + return -r, nil + case string: + return -r, nil + } + + if c != 0 { + return c * r, nil + } + + case float64: + switch y := yi.(type) { + case nil, bool: + return r, nil + case int64: + c = collateFloat(x, float64(y)) + case uint64: + c = collateFloat(x, float64(y)) + case float64: + c = collateFloat(x, y) + case complex128: + c = collateComplex(complex(x, 0), y) + case []byte: + return -r, nil + case string: + return -r, nil + } + + if c != 0 { + return c * r, nil + } + + case complex128: + switch y := yi.(type) { + case nil, bool: + return r, nil + case int64: + c = collateComplex(x, complex(float64(y), 0)) + case uint64: + c = collateComplex(x, complex(float64(y), 0)) + case float64: + c = collateComplex(x, complex(y, 0)) + case complex128: + c = collateComplex(x, y) + case []byte: + return -r, nil + case string: + return -r, nil + } + + if c != 0 { + return c * r, nil + } + + case []byte: + switch y := yi.(type) { + case nil, bool, int64, uint64, float64, complex128: + return r, nil + case []byte: + c = bytes.Compare(x, y) + case string: + return -r, nil + } + + if c != 0 { + return c * r, nil + } + + case string: + switch y := yi.(type) { + case nil, bool, int64, uint64, float64, complex128: + return r, nil + case []byte: + return r, nil + case string: + switch { + case strCollate != nil: + c = strCollate(x, y) + case x < y: + return -r, nil + case x == y: + c = 0 + case x > y: + return r, nil + } + } + + if c != 0 { + return c * r, nil + } + } + } + + if nx == ny { + return 0, nil + } + + return -r, nil +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/gb_test.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/gb_test.go new file mode 100644 index 000000000..a9923ca56 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/gb_test.go @@ -0,0 +1,364 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Utilities to encode/decode and collate Go predeclared scalar types. The +// encoding format reused the one used by the "encoding/gob" package. + +package lldb + +import ( + "bytes" + "math" + "testing" +) + +const s256 = "" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + + "0123456789abcdef" + +func TestEncodeDecodeScalars(t *testing.T) { + table := []struct{ v, exp interface{} }{ + {nil, "00"}, + {false, "01"}, + {true, "02"}, + {math.Float64frombits(0), []byte{gbFloat0}}, + {17., []byte{gbFloat2, 0x31, 0x40}}, + {math.Float64frombits(0x4031320000000000), []byte{gbFloat3, 0x32, 0x31, 0x40}}, + {math.Float64frombits(0x4031323300000000), []byte{gbFloat4, 0x33, 0x32, 0x31, 0x40}}, + {math.Float64frombits(0x4031323334000000), []byte{gbFloat5, 0x34, 0x33, 0x32, 0x31, 0x40}}, + {math.Float64frombits(0x4031323334350000), []byte{gbFloat6, 0x35, 0x34, 0x33, 0x32, 0x31, 0x40}}, + {math.Float64frombits(0x4031323334353600), []byte{gbFloat7, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x40}}, + {math.Float64frombits(0x4031323334353637), []byte{gbFloat8, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x40}}, + {0 + 0i, []byte{gbComplex0, gbComplex0}}, + {17 + 17i, []byte{gbComplex2, 0x31, 0x40, gbComplex2, 0x31, 0x40}}, + {complex(math.Float64frombits(0x4041420000000000), math.Float64frombits(0x4031320000000000)), []byte{gbComplex3, 0x42, 0x41, 0x40, gbComplex3, 0x32, 0x31, 0x40}}, + {complex(math.Float64frombits(0x4041424300000000), math.Float64frombits(0x4031323300000000)), []byte{gbComplex4, 0x43, 0x42, 0x41, 0x40, gbComplex4, 0x33, 0x32, 0x31, 0x40}}, + {complex(math.Float64frombits(0x4041424344000000), math.Float64frombits(0x4031323334000000)), []byte{gbComplex5, 0x44, 0x43, 0x42, 0x41, 0x40, gbComplex5, 0x34, 0x33, 0x32, 0x31, 0x40}}, + {complex(math.Float64frombits(0x4041424344450000), math.Float64frombits(0x4031323334350000)), []byte{gbComplex6, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, gbComplex6, 0x35, 0x34, 0x33, 0x32, 0x31, 0x40}}, + {complex(math.Float64frombits(0x4041424344454600), math.Float64frombits(0x4031323334353600)), []byte{gbComplex7, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, gbComplex7, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x40}}, + {complex(math.Float64frombits(0x4041424344454647), math.Float64frombits(0x4031323334353637)), []byte{gbComplex8, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, gbComplex8, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x40}}, + {[]byte(""), []byte{gbBytes00}}, + {[]byte("f"), []byte{gbBytes01, 'f'}}, + {[]byte("fo"), []byte{gbBytes02, 'f', 'o'}}, + {[]byte("0123456789abcdefx"), []byte{gbBytes17, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'x'}}, + {[]byte("0123456789abcdefxy"), []byte{gbBytes1, 18, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'x', 'y'}}, + {[]byte(s256[:255]), append([]byte{gbBytes1, 0xff}, []byte(s256[:255])...)}, + {[]byte(s256), append([]byte{gbBytes2, 0x00, 0xff}, []byte(s256)...)}, + {"", []byte{gbString00}}, + {"f", []byte{gbString01, 'f'}}, + {"fo", []byte{gbString02, 'f', 'o'}}, + {"0123456789abcdefx", []byte{gbString17, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'x'}}, + {"0123456789abcdefxy", []byte{gbString1, 18, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'x', 'y'}}, + {s256[:255], append([]byte{gbString1, 0xff}, []byte(s256[:255])...)}, + {s256, append([]byte{gbString2, 0x01, 0x00}, []byte(s256)...)}, + {uint64(0xff), []byte{gbUintP1, 255}}, + {uint64(0xffff), []byte{gbUintP2, 255, 255}}, + {uint64(0xffffff), []byte{gbUintP3, 255, 255, 255}}, + {uint64(0xffffffff), []byte{gbUintP4, 255, 255, 255, 255}}, + {uint64(0xffffffffff), []byte{gbUintP5, 255, 255, 255, 255, 255}}, + {uint64(0xffffffffffff), []byte{gbUintP6, 255, 255, 255, 255, 255, 255}}, + {uint64(0xffffffffffffff), []byte{gbUintP7, 255, 255, 255, 255, 255, 255, 255}}, + {uint64(0xffffffffffffffff), []byte{gbUintP8, 255, 255, 255, 255, 255, 255, 255, 255}}, + {int64(math.MinInt64), []byte{gbIntM8, 128, 0, 0, 0, 0, 0, 0, 0}}, + {-int64(0x100000000000000), []byte{gbIntM7, 0, 0, 0, 0, 0, 0, 0}}, + {-int64(0x1000000000000), []byte{gbIntM6, 0, 0, 0, 0, 0, 0}}, + {-int64(0x10000000000), []byte{gbIntM5, 0, 0, 0, 0, 0}}, + {-int64(0x100000000), []byte{gbIntM4, 0, 0, 0, 0}}, + {-int64(0x1000000), []byte{gbIntM3, 0, 0, 0}}, + {-int64(0x10000), []byte{gbIntM2, 0, 0}}, + {-int64(0x100), []byte{gbIntM1, 0}}, + {-int64(0xff), []byte{gbIntM1, 1}}, + {-int64(1), []byte{gbIntM1, 255}}, + {int64(gbIntMax + 1), []byte{gbIntP1, gbIntMax + 1}}, + {int64(0xff), []byte{gbIntP1, 255}}, + {int64(0xffff), []byte{gbIntP2, 255, 255}}, + {int64(0xffffff), []byte{gbIntP3, 255, 255, 255}}, + {int64(0xffffffff), []byte{gbIntP4, 255, 255, 255, 255}}, + {int64(0xffffffffff), []byte{gbIntP5, 255, 255, 255, 255, 255}}, + {int64(0xffffffffffff), []byte{gbIntP6, 255, 255, 255, 255, 255, 255}}, + {int64(0xffffffffffffff), []byte{gbIntP7, 255, 255, 255, 255, 255, 255, 255}}, + {int64(0x7fffffffffffffff), []byte{gbIntP8, 127, 255, 255, 255, 255, 255, 255, 255}}, + {int64(0), []byte{0 + gbInt0}}, + {int64(1), []byte{1 + gbInt0}}, + {int64(2), []byte{2 + gbInt0}}, + {int64(gbIntMax - 2), "fd"}, + {int64(gbIntMax - 1), "fe"}, + {int64(gbIntMax), "ff"}, + } + + for i, v := range table { + g, err := EncodeScalars(v.v) + if err != nil { + t.Fatal(i, err) + } + + var e []byte + switch x := v.exp.(type) { + case string: + e = s2b(x) + case []byte: + e = x + } + + if !bytes.Equal(g, e) { + t.Fatalf("%d %v\n|% 02x|\n|% 02x|", i, v.v, g, e) + } + + t.Logf("%#v |% 02x|", v.v, g) + + dec, err := DecodeScalars(g) + if err != nil { + t.Fatal(err) + } + + if g, e := len(dec), 1; g != e { + t.Fatalf("%d %d %#v", g, e, dec) + } + + if g, ok := dec[0].([]byte); ok { + if e := v.v.([]byte); !bytes.Equal(g, e) { + t.Fatal(g, e) + } + + continue + } + + if g, e := dec[0], v.v; g != e { + t.Fatal(g, e) + } + } +} + +func strcmp(a, b string) (r int) { + if a < b { + return -1 + } + + if a == b { + return 0 + } + + return 1 +} + +func TestCollateScalars(t *testing.T) { + // all cases must return -1 + table := []struct{ x, y []interface{} }{ + {[]interface{}{}, []interface{}{1}}, + {[]interface{}{1}, []interface{}{2}}, + {[]interface{}{1, 2}, []interface{}{2, 3}}, + + {[]interface{}{nil}, []interface{}{nil, true}}, + {[]interface{}{nil}, []interface{}{false}}, + {[]interface{}{nil}, []interface{}{nil, 1}}, + {[]interface{}{nil}, []interface{}{1}}, + {[]interface{}{nil}, []interface{}{nil, uint(1)}}, + {[]interface{}{nil}, []interface{}{uint(1)}}, + {[]interface{}{nil}, []interface{}{nil, 3.14}}, + {[]interface{}{nil}, []interface{}{3.14}}, + {[]interface{}{nil}, []interface{}{nil, 3.14 + 1i}}, + {[]interface{}{nil}, []interface{}{3.14 + 1i}}, + {[]interface{}{nil}, []interface{}{nil, []byte("foo")}}, + {[]interface{}{nil}, []interface{}{[]byte("foo")}}, + {[]interface{}{nil}, []interface{}{nil, "foo"}}, + {[]interface{}{nil}, []interface{}{"foo"}}, + + {[]interface{}{false}, []interface{}{false, false}}, + {[]interface{}{false}, []interface{}{false, true}}, + {[]interface{}{false}, []interface{}{true}}, + {[]interface{}{false}, []interface{}{false, 1}}, + {[]interface{}{false}, []interface{}{1}}, + {[]interface{}{false}, []interface{}{false, uint(1)}}, + {[]interface{}{false}, []interface{}{uint(1)}}, + {[]interface{}{false}, []interface{}{false, 1.5}}, + {[]interface{}{false}, []interface{}{1.5}}, + {[]interface{}{false}, []interface{}{false, 1.5 + 3i}}, + {[]interface{}{false}, []interface{}{1.5 + 3i}}, + {[]interface{}{false}, []interface{}{false, []byte("foo")}}, + {[]interface{}{false}, []interface{}{[]byte("foo")}}, + {[]interface{}{false}, []interface{}{false, "foo"}}, + {[]interface{}{false}, []interface{}{"foo"}}, + + {[]interface{}{1}, []interface{}{1, 2}}, + {[]interface{}{1}, []interface{}{1, 1}}, + {[]interface{}{1}, []interface{}{1, uint(2)}}, + {[]interface{}{1}, []interface{}{uint(2)}}, + {[]interface{}{1}, []interface{}{1, 1.1}}, + {[]interface{}{1}, []interface{}{1.1}}, + {[]interface{}{1}, []interface{}{1, 1.1 + 2i}}, + {[]interface{}{1}, []interface{}{1.1 + 2i}}, + {[]interface{}{1}, []interface{}{1, []byte("foo")}}, + {[]interface{}{1}, []interface{}{[]byte("foo")}}, + {[]interface{}{1}, []interface{}{1, "foo"}}, + {[]interface{}{1}, []interface{}{"foo"}}, + + {[]interface{}{uint(1)}, []interface{}{uint(1), uint(1)}}, + {[]interface{}{uint(1)}, []interface{}{uint(2)}}, + {[]interface{}{uint(1)}, []interface{}{uint(1), 2.}}, + {[]interface{}{uint(1)}, []interface{}{2.}}, + {[]interface{}{uint(1)}, []interface{}{uint(1), 2. + 0i}}, + {[]interface{}{uint(1)}, []interface{}{2. + 0i}}, + {[]interface{}{uint(1)}, []interface{}{uint(1), []byte("foo")}}, + {[]interface{}{uint(1)}, []interface{}{[]byte("foo")}}, + {[]interface{}{uint(1)}, []interface{}{uint(1), "foo"}}, + {[]interface{}{uint(1)}, []interface{}{"foo"}}, + + {[]interface{}{1.}, []interface{}{1., 1}}, + {[]interface{}{1.}, []interface{}{2}}, + {[]interface{}{1.}, []interface{}{1., uint(1)}}, + {[]interface{}{1.}, []interface{}{uint(2)}}, + {[]interface{}{1.}, []interface{}{1., 1.}}, + {[]interface{}{1.}, []interface{}{1.1}}, + {[]interface{}{1.}, []interface{}{1., []byte("foo")}}, + {[]interface{}{1.}, []interface{}{[]byte("foo")}}, + {[]interface{}{1.}, []interface{}{1., "foo"}}, + {[]interface{}{1.}, []interface{}{"foo"}}, + + {[]interface{}{1 + 2i}, []interface{}{1 + 2i, 1}}, + {[]interface{}{1 + 2i}, []interface{}{2}}, + {[]interface{}{1 + 2i}, []interface{}{1 + 2i, uint(1)}}, + {[]interface{}{1 + 2i}, []interface{}{uint(2)}}, + {[]interface{}{1 + 2i}, []interface{}{1 + 2i, 1.1}}, + {[]interface{}{1 + 2i}, []interface{}{1.1}}, + {[]interface{}{1 + 2i}, []interface{}{1 + 2i, []byte("foo")}}, + {[]interface{}{1 + 2i}, []interface{}{[]byte("foo")}}, + {[]interface{}{1 + 2i}, []interface{}{1 + 2i, "foo"}}, + {[]interface{}{1 + 2i}, []interface{}{"foo"}}, + + {[]interface{}{[]byte("bar")}, []interface{}{[]byte("bar"), []byte("bar")}}, + {[]interface{}{[]byte("bar")}, []interface{}{[]byte("foo")}}, + {[]interface{}{[]byte("bar")}, []interface{}{[]byte("c")}}, + {[]interface{}{[]byte("bar")}, []interface{}{[]byte("bas")}}, + {[]interface{}{[]byte("bar")}, []interface{}{[]byte("bara")}}, + + {[]interface{}{[]byte("bar")}, []interface{}{"bap"}}, + {[]interface{}{[]byte("bar")}, []interface{}{"bar"}}, + {[]interface{}{[]byte("bar")}, []interface{}{"bas"}}, + + {[]interface{}{"bar"}, []interface{}{"bar", "bar"}}, + {[]interface{}{"bar"}, []interface{}{"foo"}}, + {[]interface{}{"bar"}, []interface{}{"c"}}, + {[]interface{}{"bar"}, []interface{}{"bas"}}, + {[]interface{}{"bar"}, []interface{}{"bara"}}, + + {[]interface{}{1 + 2i}, []interface{}{1 + 3i}}, + {[]interface{}{int64(math.MaxInt64)}, []interface{}{uint64(math.MaxInt64 + 1)}}, + {[]interface{}{int8(1)}, []interface{}{int16(2)}}, + {[]interface{}{int32(1)}, []interface{}{uint8(2)}}, + {[]interface{}{uint16(1)}, []interface{}{uint32(2)}}, + {[]interface{}{float32(1)}, []interface{}{complex(float32(2), 0)}}, + + // resolved bugs + {[]interface{}{"Customer"}, []interface{}{"Date"}}, + {[]interface{}{"Customer"}, []interface{}{"Items", 1, "Quantity"}}, + } + + more := []interface{}{42, nil, 1, uint(2), 3.0, 4 + 5i, "..."} + + collate := func(x, y []interface{}, strCollate func(string, string) int) (r int) { + var err error + r, err = Collate(x, y, strCollate) + if err != nil { + t.Fatal(err) + } + + return + } + + for _, scf := range []func(string, string) int{nil, strcmp} { + for _, prefix := range more { + for i, test := range table { + var x, y []interface{} + if prefix != 42 { + x = append(x, prefix) + y = append(y, prefix) + } + x = append(x, test.x...) + y = append(y, test.y...) + + // cmp(x, y) == -1 + if g, e := collate(x, y, scf), -1; g != e { + t.Fatal(i, g, e, x, y) + } + + // cmp(y, x) == 1 + if g, e := collate(y, x, scf), 1; g != e { + t.Fatal(i, g, e, y, x) + } + + src := x + for ix := len(src) - 1; ix > 0; ix-- { + if g, e := collate(src[:ix], src[:ix], scf), 0; g != e { + t.Fatal(ix, g, e) + } + + if g, e := collate(src[:ix], src, scf), -1; g != e { + t.Fatal(ix, g, e) + } + + } + + src = y + for ix := len(src) - 1; ix > 0; ix-- { + if g, e := collate(src[:ix], src[:ix], scf), 0; g != e { + t.Fatal(ix, g, e) + } + + if g, e := collate(src[:ix], src, scf), -1; g != e { + t.Fatal(ix, g, e) + } + + } + } + } + } +} + +func TestEncodingBug(t *testing.T) { + bits := uint64(0) + for i := 0; i <= 64; i++ { + encoded, err := EncodeScalars(math.Float64frombits(bits)) + if err != nil { + t.Fatal(err) + } + + t.Logf("bits %016x, enc |% x|", bits, encoded) + decoded, err := DecodeScalars(encoded) + if err != nil { + t.Fatal(err) + } + + if g, e := len(decoded), 1; g != e { + t.Fatal(g, e) + } + + f, ok := decoded[0].(float64) + if !ok { + t.Fatal(err) + } + + if g, e := math.Float64bits(f), bits; g != e { + t.Fatal(err) + } + + t.Log(f) + + bits >>= 1 + bits |= 1 << 63 + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/lldb.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/lldb.go new file mode 100644 index 000000000..8f77ec8ad --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/lldb.go @@ -0,0 +1,155 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package lldb (WIP) implements a low level database engine. The database +// model used could be considered a specific implementation of some small(est) +// intersection of models listed in [1]. As a settled term is lacking, it'll be +// called here a 'Virtual memory model' (VMM). +// +// Experimental release notes +// +// This is an experimental release. Don't open a DB from two applications or +// two instances of an application - it will get corrupted (no file locking is +// implemented and this task is delegated to lldb's clients). +// +// WARNING: THE LLDB API IS SUBJECT TO CHANGE. +// +// Filers +// +// A Filer is an abstraction of storage. A Filer may be a part of some process' +// virtual address space, an OS file, a networked, remote file etc. Persistence +// of the storage is optional, opaque to VMM and it is specific to a concrete +// Filer implementation. +// +// Space management +// +// Mechanism to allocate, reallocate (resize), deallocate (and later reclaim +// the unused) contiguous parts of a Filer, called blocks. Blocks are +// identified and referred to by a handle, an int64. +// +// BTrees +// +// In addition to the VMM like services, lldb provides volatile and +// non-volatile BTrees. Keys and values of a BTree are limited in size to 64kB +// each (a bit more actually). Support for larger keys/values, if desired, can +// be built atop a BTree to certain limits. +// +// Handles vs pointers +// +// A handle is the abstracted storage counterpart of a memory address. There +// is one fundamental difference, though. Resizing a block never results in a +// change to the handle which refers to the resized block, so a handle is more +// akin to an unique numeric id/key. Yet it shares one property of pointers - +// handles can be associated again with blocks after the original handle block +// was deallocated. In other words, a handle uniqueness domain is the state of +// the database and is not something comparable to e.g. an ever growing +// numbering sequence. +// +// Also, as with memory pointers, dangling handles can be created and blocks +// overwritten when such handles are used. Using a zero handle to refer to a +// block will not panic; however, the resulting error is effectively the same +// exceptional situation as dereferencing a nil pointer. +// +// Blocks +// +// Allocated/used blocks, are limited in size to only a little bit more than +// 64kB. Bigger semantic entities/structures must be built in lldb's client +// code. The content of a block has no semantics attached, it's only a fully +// opaque `[]byte`. +// +// Scalars +// +// Use of "scalars" applies to EncodeScalars, DecodeScalars and Collate. Those +// first two "to bytes" and "from bytes" functions are suggested for handling +// multi-valued Allocator content items and/or keys/values of BTrees (using +// Collate for keys). Types called "scalar" are: +// +// nil (the typeless one) +// bool +// all integral types: [u]int8, [u]int16, [u]int32, [u]int, [u]int64 +// all floating point types: float32, float64 +// all complex types: complex64, complex128 +// []byte (64kB max) +// string (64kb max) +// +// Specific implementations +// +// Included are concrete implementations of some of the VMM interfaces included +// to ease serving simple client code or for testing and possibly as an +// example. More details in the documentation of such implementations. +// +// [1]: http://en.wikipedia.org/wiki/Database_model +package lldb + +const ( + fltSz = 0x70 // size of the FLT + maxShort = 251 + maxRq = 65787 + maxFLTRq = 4112 + maxHandle = 1<<56 - 1 + atomLen = 16 + tagUsedLong = 0xfc + tagUsedRelocated = 0xfd + tagFreeShort = 0xfe + tagFreeLong = 0xff + tagNotCompressed = 0 + tagCompressed = 1 +) + +// Content size n -> blocksize in atoms. +func n2atoms(n int) int { + if n > maxShort { + n += 2 + } + return (n+1)/16 + 1 +} + +// Content size n -> number of padding zeros. +func n2padding(n int) int { + if n > maxShort { + n += 2 + } + return 15 - (n+1)&15 +} + +// Handle <-> offset +func h2off(h int64) int64 { return (h + 6) * 16 } +func off2h(off int64) int64 { return off/16 - 6 } + +// Get a 7B int64 from b +func b2h(b []byte) (h int64) { + for _, v := range b[:7] { + h = h<<8 | int64(v) + } + return +} + +// Put a 7B int64 into b +func h2b(b []byte, h int64) []byte { + for i := range b[:7] { + b[i], h = byte(h>>48), h<<8 + } + return b +} + +// Content length N (must be in [252, 65787]) to long used block M field. +func n2m(n int) (m int) { + return n % 0x10000 +} + +// Long used block M (must be in [0, 65535]) field to content length N. +func m2n(m int) (n int) { + if m <= maxShort { + m += 0x10000 + } + return m +} + +func bpack(a []byte) []byte { + if cap(a) > len(a) { + return append([]byte(nil), a...) + } + + return a +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/lldb_test.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/lldb_test.go new file mode 100644 index 000000000..408125a59 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/lldb_test.go @@ -0,0 +1,217 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lldb + +import ( + "bytes" + "encoding/hex" + "fmt" + "path" + "runtime" + "strings" + "testing" +) + +var dbg = func(s string, va ...interface{}) { + _, fn, fl, _ := runtime.Caller(1) + fmt.Printf("%s:%d: ", path.Base(fn), fl) + fmt.Printf(s+"\n", va...) +} + +func use(...interface{}) {} + +func TestN2Atoms(t *testing.T) { + tab := []struct{ n, a int }{ + {0, 1}, + {1, 1}, + {2, 1}, + {3, 1}, + {4, 1}, + {5, 1}, + {6, 1}, + {7, 1}, + {8, 1}, + {9, 1}, + {10, 1}, + {11, 1}, + {12, 1}, + {13, 1}, + {14, 1}, + + {15, 2}, + {16, 2}, + {17, 2}, + {18, 2}, + {19, 2}, + {20, 2}, + {21, 2}, + {22, 2}, + {23, 2}, + {24, 2}, + {25, 2}, + {26, 2}, + {27, 2}, + {28, 2}, + {29, 2}, + {30, 2}, + + {31, 3}, + + {252, 16}, + {253, 17}, + {254, 17}, + {255, 17}, + {256, 17}, + {257, 17}, + {258, 17}, + {259, 17}, + {260, 17}, + {261, 17}, + {262, 17}, + {263, 17}, + {264, 17}, + {265, 17}, + {266, 17}, + {267, 17}, + {268, 17}, + {269, 18}, + {65532, 4096}, + {65533, 4097}, + {65787, 4112}, + } + + for i, test := range tab { + if g, e := n2atoms(test.n), test.a; g != e { + t.Errorf("(%d) %d %d %d", i, test.n, g, e) + } + } +} + +func TestN2Padding(t *testing.T) { + tab := []struct{ n, p int }{ + {0, 14}, + {1, 13}, + {2, 12}, + {3, 11}, + {4, 10}, + {5, 9}, + {6, 8}, + {7, 7}, + {8, 6}, + {9, 5}, + {10, 4}, + {11, 3}, + {12, 2}, + {13, 1}, + {14, 0}, + + {15, 15}, + {16, 14}, + {17, 13}, + {18, 12}, + {19, 11}, + {20, 10}, + {21, 9}, + {22, 8}, + {23, 7}, + {24, 6}, + {25, 5}, + {26, 4}, + {27, 3}, + {28, 2}, + {29, 1}, + {30, 0}, + + {31, 15}, + + {252, 0}, + {253, 15}, + {254, 14}, + {255, 13}, + {256, 12}, + {257, 11}, + {258, 10}, + {259, 9}, + {260, 8}, + {261, 7}, + {262, 6}, + {263, 5}, + {264, 4}, + {265, 3}, + {266, 2}, + {267, 1}, + {268, 0}, + {269, 15}, + } + + for i, test := range tab { + if g, e := n2padding(test.n), test.p; g != e { + t.Errorf("(%d) %d %d %d", i, test.n, g, e) + } + } +} + +func TestH2Off(t *testing.T) { + tab := []struct{ h, off int64 }{ + {-1, fltSz - 32}, + {0, fltSz - 16}, + {1, fltSz + 0}, + {2, fltSz + 16}, + {3, fltSz + 32}, + } + + for i, test := range tab { + if g, e := h2off(test.h), test.off; g != e { + t.Error("h2off", i, g, e) + } + if g, e := off2h(test.off), test.h; g != e { + t.Error("off2h", i, g, e) + } + } +} + +func TestB2H(t *testing.T) { + tab := []struct { + b []byte + h int64 + }{ + {[]byte{0, 0, 0, 0, 0, 0, 0}, 0}, + {[]byte{0, 0, 0, 0, 0, 0, 1}, 1}, + {[]byte{0, 0, 0, 0, 0, 0, 1, 2}, 1}, + {[]byte{0, 0, 0, 0, 0, 0x32, 0x10}, 0x3210}, + {[]byte{0, 0, 0, 0, 0x54, 0x32, 0x10}, 0x543210}, + {[]byte{0, 0, 0, 0x76, 0x54, 0x32, 0x10}, 0x76543210}, + {[]byte{0, 0, 0x98, 0x76, 0x54, 0x32, 0x10}, 0x9876543210}, + {[]byte{0, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, 0xba9876543210}, + {[]byte{0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}, 0xdcba9876543210}, + } + + for i, test := range tab { + if g, e := b2h(test.b), test.h; g != e { + t.Errorf("b2h: %d %#8x %#8x", i, g, e) + } + var g [7]byte + h2b(g[:], test.h) + if e := test.b; !bytes.Equal(g[:], e[:7]) { + t.Errorf("b2h: %d g: % 0x e: % 0x", i, g, e) + } + } +} + +func s2b(s string) []byte { + if s == "" { + return nil + } + + s = strings.Replace(s, " ", "", -1) + if n := len(s) & 1; n != 0 { + panic(n) + } + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/memfiler.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/memfiler.go new file mode 100644 index 000000000..417e92f3a --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/memfiler.go @@ -0,0 +1,344 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// A memory-only implementation of Filer. + +/* + +pgBits: 8 +BenchmarkMemFilerWrSeq 100000 19430 ns/op 1646.93 MB/s +BenchmarkMemFilerRdSeq 100000 17390 ns/op 1840.13 MB/s +BenchmarkMemFilerWrRand 1000000 1903 ns/op 133.94 MB/s +BenchmarkMemFilerRdRand 1000000 1153 ns/op 221.16 MB/s + +pgBits: 9 +BenchmarkMemFilerWrSeq 100000 16195 ns/op 1975.80 MB/s +BenchmarkMemFilerRdSeq 200000 13011 ns/op 2459.39 MB/s +BenchmarkMemFilerWrRand 1000000 2248 ns/op 227.28 MB/s +BenchmarkMemFilerRdRand 1000000 1177 ns/op 433.94 MB/s + +pgBits: 10 +BenchmarkMemFilerWrSeq 100000 16169 ns/op 1979.04 MB/s +BenchmarkMemFilerRdSeq 200000 12673 ns/op 2524.91 MB/s +BenchmarkMemFilerWrRand 1000000 5550 ns/op 184.30 MB/s +BenchmarkMemFilerRdRand 1000000 1699 ns/op 601.79 MB/s + +pgBits: 11 +BenchmarkMemFilerWrSeq 100000 13449 ns/op 2379.31 MB/s +BenchmarkMemFilerRdSeq 200000 12058 ns/op 2653.80 MB/s +BenchmarkMemFilerWrRand 500000 4335 ns/op 471.47 MB/s +BenchmarkMemFilerRdRand 1000000 2843 ns/op 719.47 MB/s + +pgBits: 12 +BenchmarkMemFilerWrSeq 200000 11976 ns/op 2672.00 MB/s +BenchmarkMemFilerRdSeq 200000 12255 ns/op 2611.06 MB/s +BenchmarkMemFilerWrRand 200000 8058 ns/op 507.14 MB/s +BenchmarkMemFilerRdRand 500000 4365 ns/op 936.15 MB/s + +pgBits: 13 +BenchmarkMemFilerWrSeq 200000 10852 ns/op 2948.69 MB/s +BenchmarkMemFilerRdSeq 200000 11561 ns/op 2767.77 MB/s +BenchmarkMemFilerWrRand 200000 9748 ns/op 840.15 MB/s +BenchmarkMemFilerRdRand 500000 7236 ns/op 1131.59 MB/s + +pgBits: 14 +BenchmarkMemFilerWrSeq 200000 10328 ns/op 3098.12 MB/s +BenchmarkMemFilerRdSeq 200000 11292 ns/op 2833.66 MB/s +BenchmarkMemFilerWrRand 100000 16768 ns/op 978.75 MB/s +BenchmarkMemFilerRdRand 200000 13033 ns/op 1258.43 MB/s + +pgBits: 15 +BenchmarkMemFilerWrSeq 200000 10309 ns/op 3103.93 MB/s +BenchmarkMemFilerRdSeq 200000 11126 ns/op 2876.12 MB/s +BenchmarkMemFilerWrRand 50000 31985 ns/op 1021.74 MB/s +BenchmarkMemFilerRdRand 100000 25217 ns/op 1297.65 MB/s + +pgBits: 16 +BenchmarkMemFilerWrSeq 200000 10324 ns/op 3099.45 MB/s +BenchmarkMemFilerRdSeq 200000 11201 ns/op 2856.80 MB/s +BenchmarkMemFilerWrRand 20000 55226 ns/op 1184.76 MB/s +BenchmarkMemFilerRdRand 50000 48316 ns/op 1355.16 MB/s + +pgBits: 17 +BenchmarkMemFilerWrSeq 200000 10377 ns/op 3083.53 MB/s +BenchmarkMemFilerRdSeq 200000 11018 ns/op 2904.18 MB/s +BenchmarkMemFilerWrRand 10000 143425 ns/op 913.12 MB/s +BenchmarkMemFilerRdRand 20000 95267 ns/op 1376.99 MB/s + +pgBits: 18 +BenchmarkMemFilerWrSeq 200000 10312 ns/op 3102.96 MB/s +BenchmarkMemFilerRdSeq 200000 11069 ns/op 2890.84 MB/s +BenchmarkMemFilerWrRand 5000 280910 ns/op 934.14 MB/s +BenchmarkMemFilerRdRand 10000 188500 ns/op 1388.17 MB/s + +*/ + +package lldb + +import ( + "bytes" + "fmt" + "io" + + "github.com/cznic/fileutil" + "github.com/cznic/mathutil" +) + +const ( + pgBits = 16 + pgSize = 1 << pgBits + pgMask = pgSize - 1 +) + +var _ Filer = &MemFiler{} // Ensure MemFiler is a Filer. + +type memFilerMap map[int64]*[pgSize]byte + +// MemFiler is a memory backed Filer. It implements BeginUpdate, EndUpdate and +// Rollback as no-ops. MemFiler is not automatically persistent, but it has +// ReadFrom and WriteTo methods. +type MemFiler struct { + m memFilerMap + nest int + size int64 +} + +// NewMemFiler returns a new MemFiler. +func NewMemFiler() *MemFiler { + return &MemFiler{m: memFilerMap{}} +} + +// BeginUpdate implements Filer. +func (f *MemFiler) BeginUpdate() error { + f.nest++ + return nil +} + +// Close implements Filer. +func (f *MemFiler) Close() (err error) { + if f.nest != 0 { + return &ErrPERM{(f.Name() + ":Close")} + } + + return +} + +// EndUpdate implements Filer. +func (f *MemFiler) EndUpdate() (err error) { + if f.nest == 0 { + return &ErrPERM{(f.Name() + ": EndUpdate")} + } + + f.nest-- + return +} + +// Name implements Filer. +func (f *MemFiler) Name() string { + return fmt.Sprintf("%p.memfiler", f) +} + +// PunchHole implements Filer. +func (f *MemFiler) PunchHole(off, size int64) (err error) { + if off < 0 { + return &ErrINVAL{f.Name() + ": PunchHole off", off} + } + + if size < 0 || off+size > f.size { + return &ErrINVAL{f.Name() + ": PunchHole size", size} + } + + first := off >> pgBits + if off&pgMask != 0 { + first++ + } + off += size - 1 + last := off >> pgBits + if off&pgMask != 0 { + last-- + } + if limit := f.size >> pgBits; last > limit { + last = limit + } + for pg := first; pg <= last; pg++ { + delete(f.m, pg) + } + return +} + +var zeroPage [pgSize]byte + +// ReadAt implements Filer. +func (f *MemFiler) ReadAt(b []byte, off int64) (n int, err error) { + avail := f.size - off + pgI := off >> pgBits + pgO := int(off & pgMask) + rem := len(b) + if int64(rem) >= avail { + rem = int(avail) + err = io.EOF + } + for rem != 0 && avail > 0 { + pg := f.m[pgI] + if pg == nil { + pg = &zeroPage + } + nc := copy(b[:mathutil.Min(rem, pgSize)], pg[pgO:]) + pgI++ + pgO = 0 + rem -= nc + n += nc + b = b[nc:] + } + return +} + +// ReadFrom is a helper to populate MemFiler's content from r. 'n' reports the +// number of bytes read from 'r'. +func (f *MemFiler) ReadFrom(r io.Reader) (n int64, err error) { + if err = f.Truncate(0); err != nil { + return + } + + var ( + b [pgSize]byte + rn int + off int64 + ) + + var rerr error + for rerr == nil { + if rn, rerr = r.Read(b[:]); rn != 0 { + f.WriteAt(b[:rn], off) + off += int64(rn) + n += int64(rn) + } + } + if !fileutil.IsEOF(rerr) { + err = rerr + } + return +} + +// Rollback implements Filer. +func (f *MemFiler) Rollback() (err error) { return } + +// Size implements Filer. +func (f *MemFiler) Size() (int64, error) { + return f.size, nil +} + +// Sync implements Filer. +func (f *MemFiler) Sync() error { + return nil +} + +// Truncate implements Filer. +func (f *MemFiler) Truncate(size int64) (err error) { + switch { + case size < 0: + return &ErrINVAL{"Truncate size", size} + case size == 0: + f.m = memFilerMap{} + f.size = 0 + return + } + + first := size >> pgBits + if size&pgMask != 0 { + first++ + } + last := f.size >> pgBits + if f.size&pgMask != 0 { + last++ + } + for ; first < last; first++ { + delete(f.m, first) + } + + f.size = size + return +} + +// WriteAt implements Filer. +func (f *MemFiler) WriteAt(b []byte, off int64) (n int, err error) { + pgI := off >> pgBits + pgO := int(off & pgMask) + n = len(b) + rem := n + var nc int + for rem != 0 { + if pgO == 0 && rem >= pgSize && bytes.Equal(b[:pgSize], zeroPage[:]) { + delete(f.m, pgI) + nc = pgSize + } else { + pg := f.m[pgI] + if pg == nil { + pg = new([pgSize]byte) + f.m[pgI] = pg + } + nc = copy((*pg)[pgO:], b) + } + pgI++ + pgO = 0 + rem -= nc + b = b[nc:] + } + f.size = mathutil.MaxInt64(f.size, off+int64(n)) + return +} + +// WriteTo is a helper to copy/persist MemFiler's content to w. If w is also +// an io.WriterAt then WriteTo may attempt to _not_ write any big, for some +// value of big, runs of zeros, i.e. it will attempt to punch holes, where +// possible, in `w` if that happens to be a freshly created or to zero length +// truncated OS file. 'n' reports the number of bytes written to 'w'. +func (f *MemFiler) WriteTo(w io.Writer) (n int64, err error) { + var ( + b [pgSize]byte + wn, rn int + off int64 + rerr error + ) + + if wa, ok := w.(io.WriterAt); ok { + lastPgI := f.size >> pgBits + for pgI := int64(0); pgI <= lastPgI; pgI++ { + sz := pgSize + if pgI == lastPgI { + sz = int(f.size & pgMask) + } + pg := f.m[pgI] + if pg != nil { + wn, err = wa.WriteAt(pg[:sz], off) + if err != nil { + return + } + + n += int64(wn) + off += int64(sz) + if wn != sz { + return n, io.ErrShortWrite + } + } + } + return + } + + var werr error + for rerr == nil { + if rn, rerr = f.ReadAt(b[:], off); rn != 0 { + off += int64(rn) + if wn, werr = w.Write(b[:rn]); werr != nil { + return n, werr + } + + n += int64(wn) + } + } + if !fileutil.IsEOF(rerr) { + err = rerr + } + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/memfiler_test.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/memfiler_test.go new file mode 100644 index 000000000..319f4bbbf --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/memfiler_test.go @@ -0,0 +1,132 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lldb + +import ( + "bytes" + "math/rand" + "testing" +) + +// Test automatic page releasing (hole punching) of zero pages +func TestMemFilerWriteAt(t *testing.T) { + f := NewMemFiler() + + // Add page index 0 + if _, err := f.WriteAt([]byte{1}, 0); err != nil { + t.Fatal(err) + } + + if g, e := len(f.m), 1; g != e { + t.Fatal(g, e) + } + + // Add page index 1 + if _, err := f.WriteAt([]byte{2}, pgSize); err != nil { + t.Fatal(err) + } + + if g, e := len(f.m), 2; g != e { + t.Fatal(g, e) + } + + // Add page index 2 + if _, err := f.WriteAt([]byte{3}, 2*pgSize); err != nil { + t.Fatal(err) + } + + if g, e := len(f.m), 3; g != e { + t.Fatal(g, e) + } + + // Remove page index 1 + if _, err := f.WriteAt(make([]byte, 2*pgSize), pgSize/2); err != nil { + t.Fatal(err) + } + + if g, e := len(f.m), 2; g != e { + t.Logf("%#v", f.m) + t.Fatal(g, e) + } + + if err := f.Truncate(1); err != nil { + t.Fatal(err) + } + + if g, e := len(f.m), 1; g != e { + t.Logf("%#v", f.m) + t.Fatal(g, e) + } + + if err := f.Truncate(0); err != nil { + t.Fatal(err) + } + + if g, e := len(f.m), 0; g != e { + t.Logf("%#v", f.m) + t.Fatal(g, e) + } +} + +func TestMemFilerWriteTo(t *testing.T) { + const max = 1e5 + var b [max]byte + rng := rand.New(rand.NewSource(42)) + for sz := 0; sz < 1e5; sz += 2053 { + for i := range b[:sz] { + b[i] = byte(rng.Int()) + } + f := NewMemFiler() + if n, err := f.WriteAt(b[:sz], 0); n != sz || err != nil { + t.Fatal(n, err) + } + + var buf bytes.Buffer + if n, err := f.WriteTo(&buf); n != int64(sz) || err != nil { + t.Fatal(n, err) + } + + if !bytes.Equal(b[:sz], buf.Bytes()) { + t.Fatal("content differs") + } + } +} + +func TestMemFilerReadFromWriteTo(t *testing.T) { + const ( + sz = 1e2 * pgSize + hole = 1e1 * pgSize + ) + rng := rand.New(rand.NewSource(42)) + data := make([]byte, sz) + for i := range data { + data[i] = byte(rng.Int()) + } + f := NewMemFiler() + buf := bytes.NewBuffer(data) + if n, err := f.ReadFrom(buf); n != int64(len(data)) || err != nil { + t.Fatal(n, err) + } + + buf = bytes.NewBuffer(nil) + if n, err := f.WriteTo(buf); n != int64(len(data)) || err != nil { + t.Fatal(n, err) + } + + rd := buf.Bytes() + if !bytes.Equal(data, rd) { + t.Fatal("corrupted data") + } + + n0 := len(f.m) + data = make([]byte, hole) + f.WriteAt(data, sz/2) + n := len(f.m) + t.Log(n0, n) + d := n0 - n + if d*pgSize < hole-2 || d*pgSize > hole { + t.Fatal(n0, n, d) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/osfiler.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/osfiler.go new file mode 100644 index 000000000..d6e189a18 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/osfiler.go @@ -0,0 +1,130 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lldb + +import ( + "io" + "os" + + "github.com/cznic/mathutil" +) + +var _ Filer = (*OSFiler)(nil) + +// OSFile is an os.File like minimal set of methods allowing to construct a +// Filer. +type OSFile interface { + Name() string + Stat() (fi os.FileInfo, err error) + Sync() (err error) + Truncate(size int64) (err error) + io.Closer + io.Reader + io.ReaderAt + io.Seeker + io.Writer + io.WriterAt +} + +// OSFiler is like a SimpleFileFiler but based on an OSFile. +type OSFiler struct { + f OSFile + nest int + size int64 // not set if < 0 +} + +// NewOSFiler returns a Filer from an OSFile. This Filer is like the +// SimpleFileFiler, it does not implement the transaction related methods. +func NewOSFiler(f OSFile) (r *OSFiler) { + return &OSFiler{ + f: f, + size: -1, + } +} + +// BeginUpdate implements Filer. +func (f *OSFiler) BeginUpdate() (err error) { + f.nest++ + return nil +} + +// Close implements Filer. +func (f *OSFiler) Close() (err error) { + if f.nest != 0 { + return &ErrPERM{(f.Name() + ":Close")} + } + + return f.f.Close() +} + +// EndUpdate implements Filer. +func (f *OSFiler) EndUpdate() (err error) { + if f.nest == 0 { + return &ErrPERM{(f.Name() + ":EndUpdate")} + } + + f.nest-- + return +} + +// Name implements Filer. +func (f *OSFiler) Name() string { + return f.f.Name() +} + +// PunchHole implements Filer. +func (f *OSFiler) PunchHole(off, size int64) (err error) { + return +} + +// ReadAt implements Filer. +func (f *OSFiler) ReadAt(b []byte, off int64) (n int, err error) { + return f.f.ReadAt(b, off) +} + +// Rollback implements Filer. +func (f *OSFiler) Rollback() (err error) { return } + +// Size implements Filer. +func (f *OSFiler) Size() (n int64, err error) { + if f.size < 0 { // boot + fi, err := f.f.Stat() + if err != nil { + return 0, err + } + + f.size = fi.Size() + } + return f.size, nil +} + +// Sync implements Filer. +func (f *OSFiler) Sync() (err error) { + return f.f.Sync() +} + +// Truncate implements Filer. +func (f *OSFiler) Truncate(size int64) (err error) { + if size < 0 { + return &ErrINVAL{"Truncate size", size} + } + + f.size = size + return f.f.Truncate(size) +} + +// WriteAt implements Filer. +func (f *OSFiler) WriteAt(b []byte, off int64) (n int, err error) { + if f.size < 0 { // boot + fi, err := os.Stat(f.f.Name()) + if err != nil { + return 0, err + } + + f.size = fi.Size() + } + f.size = mathutil.MaxInt64(f.size, int64(len(b))+off) + return f.f.WriteAt(b, off) +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/simplefilefiler.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/simplefilefiler.go new file mode 100644 index 000000000..de32e6491 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/simplefilefiler.go @@ -0,0 +1,123 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// A basic os.File backed Filer. + +package lldb + +import ( + "os" + + "github.com/cznic/fileutil" + "github.com/cznic/mathutil" +) + +var _ Filer = &SimpleFileFiler{} // Ensure SimpleFileFiler is a Filer. + +// SimpleFileFiler is an os.File backed Filer intended for use where structural +// consistency can be reached by other means (SimpleFileFiler is for example +// wrapped in eg. an RollbackFiler or ACIDFiler0) or where persistence is not +// required (temporary/working data sets). +// +// SimpleFileFiler is the most simple os.File backed Filer implementation as it +// does not really implement BeginUpdate and EndUpdate/Rollback in any way +// which would protect the structural integrity of data. If misused e.g. as a +// real database storage w/o other measures, it can easily cause data loss +// when, for example, a power outage occurs or the updating process terminates +// abruptly. +type SimpleFileFiler struct { + file *os.File + nest int + size int64 // not set if < 0 +} + +// NewSimpleFileFiler returns a new SimpleFileFiler. +func NewSimpleFileFiler(f *os.File) *SimpleFileFiler { + return &SimpleFileFiler{file: f, size: -1} +} + +// BeginUpdate implements Filer. +func (f *SimpleFileFiler) BeginUpdate() error { + f.nest++ + return nil +} + +// Close implements Filer. +func (f *SimpleFileFiler) Close() (err error) { + if f.nest != 0 { + return &ErrPERM{(f.Name() + ":Close")} + } + + return f.file.Close() +} + +// EndUpdate implements Filer. +func (f *SimpleFileFiler) EndUpdate() (err error) { + if f.nest == 0 { + return &ErrPERM{(f.Name() + ":EndUpdate")} + } + + f.nest-- + return +} + +// Name implements Filer. +func (f *SimpleFileFiler) Name() string { + return f.file.Name() +} + +// PunchHole implements Filer. +func (f *SimpleFileFiler) PunchHole(off, size int64) (err error) { + return fileutil.PunchHole(f.file, off, size) +} + +// ReadAt implements Filer. +func (f *SimpleFileFiler) ReadAt(b []byte, off int64) (n int, err error) { + return f.file.ReadAt(b, off) +} + +// Rollback implements Filer. +func (f *SimpleFileFiler) Rollback() (err error) { return } + +// Size implements Filer. +func (f *SimpleFileFiler) Size() (int64, error) { + if f.size < 0 { // boot + fi, err := os.Stat(f.file.Name()) + if err != nil { + return 0, err + } + + f.size = fi.Size() + } + return f.size, nil +} + +// Sync implements Filer. +func (f *SimpleFileFiler) Sync() error { + return f.file.Sync() +} + +// Truncate implements Filer. +func (f *SimpleFileFiler) Truncate(size int64) (err error) { + if size < 0 { + return &ErrINVAL{"Truncate size", size} + } + + f.size = size + return f.file.Truncate(size) +} + +// WriteAt implements Filer. +func (f *SimpleFileFiler) WriteAt(b []byte, off int64) (n int, err error) { + if f.size < 0 { // boot + fi, err := os.Stat(f.file.Name()) + if err != nil { + return 0, err + } + + f.size = fi.Size() + } + f.size = mathutil.MaxInt64(f.size, int64(len(b))+off) + return f.file.WriteAt(b, off) +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/xact.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/xact.go new file mode 100644 index 000000000..f9ad1cbfb --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/xact.go @@ -0,0 +1,629 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Structural transactions. + +package lldb + +//DONE+ TransactionalMemoryFiler +// ---- +// Use NewRollbackFiler(myMemFiler, ...) + +/* + +bfBits: 3 +BenchmarkRollbackFiler 20000000 102 ns/op 9.73 MB/s + +bfBits: 4 +BenchmarkRollbackFiler 50000000 55.7 ns/op 17.95 MB/s + +bfBits: 5 +BenchmarkRollbackFiler 100000000 32.2 ns/op 31.06 MB/s + +bfBits: 6 +BenchmarkRollbackFiler 100000000 20.6 ns/op 48.46 MB/s + +bfBits: 7 +BenchmarkRollbackFiler 100000000 15.1 ns/op 66.12 MB/s + +bfBits: 8 +BenchmarkRollbackFiler 100000000 10.5 ns/op 95.66 MB/s + +bfBits: 9 +BenchmarkRollbackFiler 200000000 8.02 ns/op 124.74 MB/s + +bfBits: 10 +BenchmarkRollbackFiler 200000000 9.25 ns/op 108.09 MB/s + +bfBits: 11 +BenchmarkRollbackFiler 100000000 11.7 ns/op 85.47 MB/s + +bfBits: 12 +BenchmarkRollbackFiler 100000000 17.2 ns/op 57.99 MB/s + +bfBits: 13 +BenchmarkRollbackFiler 100000000 32.7 ns/op 30.58 MB/s + +bfBits: 14 +BenchmarkRollbackFiler 50000000 39.6 ns/op 25.27 MB/s + +*/ + +import ( + "fmt" + "io" + "sync" + + "github.com/cznic/fileutil" + "github.com/cznic/mathutil" +) + +var ( + _ Filer = &bitFiler{} // Ensure bitFiler is a Filer. + _ Filer = &RollbackFiler{} // ditto +) + +const ( + bfBits = 9 + bfSize = 1 << bfBits + bfMask = bfSize - 1 +) + +var ( + bitmask = [8]byte{1, 2, 4, 8, 16, 32, 64, 128} + bitZeroPage bitPage + allDirtyFlags [bfSize >> 3]byte +) + +func init() { + for i := range allDirtyFlags { + allDirtyFlags[i] = 0xff + } +} + +type ( + bitPage struct { + prev, next *bitPage + data [bfSize]byte + flags [bfSize >> 3]byte + dirty bool + } + + bitFilerMap map[int64]*bitPage + + bitFiler struct { + parent Filer + m bitFilerMap + size int64 + } +) + +func newBitFiler(parent Filer) (f *bitFiler, err error) { + sz, err := parent.Size() + if err != nil { + return + } + + return &bitFiler{parent: parent, m: bitFilerMap{}, size: sz}, nil +} + +func (f *bitFiler) BeginUpdate() error { panic("internal error") } +func (f *bitFiler) EndUpdate() error { panic("internal error") } +func (f *bitFiler) Rollback() error { panic("internal error") } +func (f *bitFiler) Sync() error { panic("internal error") } + +func (f *bitFiler) Close() (err error) { return } +func (f *bitFiler) Name() string { return fmt.Sprintf("%p.bitfiler", f) } +func (f *bitFiler) Size() (int64, error) { return f.size, nil } + +func (f *bitFiler) PunchHole(off, size int64) (err error) { + first := off >> bfBits + if off&bfMask != 0 { + first++ + } + off += size - 1 + last := off >> bfBits + if off&bfMask != 0 { + last-- + } + if limit := f.size >> bfBits; last > limit { + last = limit + } + for pgI := first; pgI <= last; pgI++ { + pg := &bitPage{} + pg.flags = allDirtyFlags + f.m[pgI] = pg + } + return +} + +func (f *bitFiler) ReadAt(b []byte, off int64) (n int, err error) { + avail := f.size - off + pgI := off >> bfBits + pgO := int(off & bfMask) + rem := len(b) + if int64(rem) >= avail { + rem = int(avail) + err = io.EOF + } + for rem != 0 && avail > 0 { + pg := f.m[pgI] + if pg == nil { + pg = &bitPage{} + if f.parent != nil { + _, err = f.parent.ReadAt(pg.data[:], off&^bfMask) + if err != nil && !fileutil.IsEOF(err) { + return + } + + err = nil + } + f.m[pgI] = pg + } + nc := copy(b[:mathutil.Min(rem, bfSize)], pg.data[pgO:]) + pgI++ + pgO = 0 + rem -= nc + n += nc + b = b[nc:] + off += int64(nc) + } + return +} + +func (f *bitFiler) Truncate(size int64) (err error) { + switch { + case size < 0: + return &ErrINVAL{"Truncate size", size} + case size == 0: + f.m = bitFilerMap{} + f.size = 0 + return + } + + first := size >> bfBits + if size&bfMask != 0 { + first++ + } + last := f.size >> bfBits + if f.size&bfMask != 0 { + last++ + } + for ; first < last; first++ { + delete(f.m, first) + } + + f.size = size + return +} + +func (f *bitFiler) WriteAt(b []byte, off int64) (n int, err error) { + off0 := off + pgI := off >> bfBits + pgO := int(off & bfMask) + n = len(b) + rem := n + var nc int + for rem != 0 { + pg := f.m[pgI] + if pg == nil { + pg = &bitPage{} + if f.parent != nil { + _, err = f.parent.ReadAt(pg.data[:], off&^bfMask) + if err != nil && !fileutil.IsEOF(err) { + return + } + + err = nil + } + f.m[pgI] = pg + } + nc = copy(pg.data[pgO:], b) + pgI++ + pg.dirty = true + for i := pgO; i < pgO+nc; i++ { + pg.flags[i>>3] |= bitmask[i&7] + } + pgO = 0 + rem -= nc + b = b[nc:] + off += int64(nc) + } + f.size = mathutil.MaxInt64(f.size, off0+int64(n)) + return +} + +func (f *bitFiler) link() { + for pgI, pg := range f.m { + nx, ok := f.m[pgI+1] + if !ok || !nx.dirty { + continue + } + + nx.prev, pg.next = pg, nx + } +} + +func (f *bitFiler) dumpDirty(w io.WriterAt) (nwr int, err error) { + f.link() + for pgI, pg := range f.m { + if !pg.dirty { + continue + } + + for pg.prev != nil && pg.prev.dirty { + pg = pg.prev + pgI-- + } + + for pg != nil && pg.dirty { + last := false + var off int64 + first := -1 + for i := 0; i < bfSize; i++ { + flag := pg.flags[i>>3]&bitmask[i&7] != 0 + switch { + case flag && !last: // Leading edge detected + off = pgI<= 0 { + i := bfSize + n, err := w.WriteAt(pg.data[first:i], off) + if n != i-first { + return 0, err + } + + nwr++ + } + + pg.dirty = false + pg = pg.next + pgI++ + } + } + return +} + +// RollbackFiler is a Filer implementing structural transaction handling. +// Structural transactions should be small and short lived because all non +// committed data are held in memory until committed or discarded by a +// Rollback. +// +// While using RollbackFiler, every intended update of the wrapped Filler, by +// WriteAt, Truncate or PunchHole, _must_ be made within a transaction. +// Attempts to do it outside of a transaction will return ErrPERM. OTOH, +// invoking ReadAt outside of a transaction is not a problem. +// +// No nested transactions: All updates within a transaction are held in memory. +// On a matching EndUpdate the updates held in memory are actually written to +// the wrapped Filer. +// +// Nested transactions: Correct data will be seen from RollbackFiler when any +// level of a nested transaction is rollbacked. The actual writing to the +// wrapped Filer happens only when the outer most transaction nesting level is +// closed. +// +// Invoking Rollback is an alternative to EndUpdate. It discards all changes +// made at the current transaction level and returns the "state" (possibly not +// yet persisted) of the Filer to what it was before the corresponding +// BeginUpdate. +// +// During an open transaction, all reads (using ReadAt) are "dirty" reads, +// seeing the uncommitted changes made to the Filer's data. +// +// Lldb databases should be based upon a RollbackFiler. +// +// With a wrapped MemFiler one gets transactional memory. With, for example a +// wrapped disk based SimpleFileFiler it protects against at least some HW +// errors - if Rollback is properly invoked on such failures and/or if there's +// some WAL or 2PC or whatever other safe mechanism based recovery procedure +// used by the client. +// +// The "real" writes to the wrapped Filer (or WAL instead) go through the +// writerAt supplied to NewRollbackFiler. +// +// List of functions/methods which are recommended to be wrapped in a +// BeginUpdate/EndUpdate structural transaction: +// +// Allocator.Alloc +// Allocator.Free +// Allocator.Realloc +// +// CreateBTree +// RemoveBTree +// BTree.Clear +// BTree.Delete +// BTree.DeleteAny +// BTree.Clear +// BTree.Extract +// BTree.Get (it can mutate the DB) +// BTree.Put +// BTree.Set +// +// NOTE: RollbackFiler is a generic solution intended to wrap Filers provided +// by this package which do not implement any of the transactional methods. +// RollbackFiler thus _does not_ invoke any of the transactional methods of its +// wrapped Filer. +// +// RollbackFiler is safe for concurrent use by multiple goroutines. +type RollbackFiler struct { + mu sync.RWMutex + inCallback bool + inCallbackMu sync.RWMutex + bitFiler *bitFiler + checkpoint func(int64) error + closed bool + f Filer + parent Filer + tlevel int // transaction nesting level, 0 == not in transaction + writerAt io.WriterAt + + // afterRollback, if not nil, is called after performing Rollback + // without errros. + afterRollback func() error +} + +// NewRollbackFiler returns a RollbackFiler wrapping f. +// +// The checkpoint parameter +// +// The checkpoint function is called after closing (by EndUpdate) the upper +// most level open transaction if all calls of writerAt were successful and the +// DB (or eg. a WAL) is thus now in a consistent state (virtually, in the ideal +// world with no write caches, no HW failures, no process crashes, ...). +// +// NOTE: In, for example, a 2PC it is necessary to reflect also the sz +// parameter as the new file size (as in the parameter to Truncate). All +// changes were successfully written already by writerAt before invoking +// checkpoint. +// +// The writerAt parameter +// +// The writerAt interface is used to commit the updates of the wrapped Filer. +// If any invocation of writerAt fails then a non nil error will be returned +// from EndUpdate and checkpoint will _not_ ne called. Neither is necessary to +// call Rollback. The rule of thumb: The [structural] transaction [level] is +// closed by invoking exactly once one of EndUpdate _or_ Rollback. +// +// It is presumed that writerAt uses WAL or 2PC or whatever other safe +// mechanism to physically commit the updates. +// +// Updates performed by invocations of writerAt are byte-precise, but not +// necessarily maximum possible length precise. IOW, for example an update +// crossing page boundaries may be performed by more than one writerAt +// invocation. No offset sorting is performed. This may change if it proves +// to be a problem. Such change would be considered backward compatible. +// +// NOTE: Using RollbackFiler, but failing to ever invoke a matching "closing" +// EndUpdate after an "opening" BeginUpdate means neither writerAt or +// checkpoint will ever get called - with all the possible data loss +// consequences. +func NewRollbackFiler(f Filer, checkpoint func(sz int64) error, writerAt io.WriterAt) (r *RollbackFiler, err error) { + if f == nil || checkpoint == nil || writerAt == nil { + return nil, &ErrINVAL{Src: "lldb.NewRollbackFiler, nil argument"} + } + + return &RollbackFiler{ + checkpoint: checkpoint, + f: f, + writerAt: writerAt, + }, nil +} + +// Implements Filer. +func (r *RollbackFiler) BeginUpdate() (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + parent := r.f + if r.tlevel != 0 { + parent = r.bitFiler + } + r.bitFiler, err = newBitFiler(parent) + if err != nil { + return + } + + r.tlevel++ + return +} + +// Implements Filer. +// +// Close will return an error if not invoked at nesting level 0. However, to +// allow emergency closing from eg. a signal handler; if Close is invoked +// within an open transaction(s), it rollbacks any non committed open +// transactions and performs the Close operation. +// +// IOW: Regardless of the transaction nesting level the Close is always +// performed but any uncommitted transaction data are lost. +func (r *RollbackFiler) Close() (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + if r.closed { + return &ErrPERM{r.f.Name() + ": Already closed"} + } + + r.closed = true + if err = r.f.Close(); err != nil { + return + } + + if r.tlevel != 0 { + err = &ErrPERM{r.f.Name() + ": Close inside an open transaction"} + } + + return +} + +// Implements Filer. +func (r *RollbackFiler) EndUpdate() (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + if r.tlevel == 0 { + return &ErrPERM{r.f.Name() + " : EndUpdate outside of a transaction"} + } + + sz, err := r.size() // Cannot call .Size() -> deadlock + if err != nil { + return + } + + r.tlevel-- + bf := r.bitFiler + parent := bf.parent + w := r.writerAt + if r.tlevel != 0 { + w = parent + } + nwr, err := bf.dumpDirty(w) + if err != nil { + return + } + + switch { + case r.tlevel == 0: + r.bitFiler = nil + if nwr == 0 { + return + } + + return r.checkpoint(sz) + default: + r.bitFiler = parent.(*bitFiler) + sz, _ := bf.Size() // bitFiler.Size() never returns err != nil + return parent.Truncate(sz) + } +} + +// Implements Filer. +func (r *RollbackFiler) Name() string { + r.mu.RLock() + defer r.mu.RUnlock() + + return r.f.Name() +} + +// Implements Filer. +func (r *RollbackFiler) PunchHole(off, size int64) error { + r.mu.Lock() + defer r.mu.Unlock() + + if r.tlevel == 0 { + return &ErrPERM{r.f.Name() + ": PunchHole outside of a transaction"} + } + + if off < 0 { + return &ErrINVAL{r.f.Name() + ": PunchHole off", off} + } + + if size < 0 || off+size > r.bitFiler.size { + return &ErrINVAL{r.f.Name() + ": PunchHole size", size} + } + + return r.bitFiler.PunchHole(off, size) +} + +// Implements Filer. +func (r *RollbackFiler) ReadAt(b []byte, off int64) (n int, err error) { + r.inCallbackMu.RLock() + defer r.inCallbackMu.RUnlock() + if !r.inCallback { + r.mu.RLock() + defer r.mu.RUnlock() + } + if r.tlevel == 0 { + return r.f.ReadAt(b, off) + } + + return r.bitFiler.ReadAt(b, off) +} + +// Implements Filer. +func (r *RollbackFiler) Rollback() (err error) { + r.mu.Lock() + defer r.mu.Unlock() + + if r.tlevel == 0 { + return &ErrPERM{r.f.Name() + ": Rollback outside of a transaction"} + } + + if r.tlevel > 1 { + r.bitFiler = r.bitFiler.parent.(*bitFiler) + } + r.tlevel-- + if f := r.afterRollback; f != nil { + r.inCallbackMu.Lock() + r.inCallback = true + r.inCallbackMu.Unlock() + defer func() { + r.inCallbackMu.Lock() + r.inCallback = false + r.inCallbackMu.Unlock() + }() + return f() + } + return +} + +func (r *RollbackFiler) size() (sz int64, err error) { + if r.tlevel == 0 { + return r.f.Size() + } + + return r.bitFiler.Size() +} + +// Implements Filer. +func (r *RollbackFiler) Size() (sz int64, err error) { + r.mu.Lock() + defer r.mu.Unlock() + + return r.size() +} + +// Implements Filer. +func (r *RollbackFiler) Sync() error { + r.mu.Lock() + defer r.mu.Unlock() + + return r.f.Sync() +} + +// Implements Filer. +func (r *RollbackFiler) Truncate(size int64) error { + r.mu.Lock() + defer r.mu.Unlock() + + if r.tlevel == 0 { + return &ErrPERM{r.f.Name() + ": Truncate outside of a transaction"} + } + + return r.bitFiler.Truncate(size) +} + +// Implements Filer. +func (r *RollbackFiler) WriteAt(b []byte, off int64) (n int, err error) { + r.mu.Lock() + defer r.mu.Unlock() + + if r.tlevel == 0 { + return 0, &ErrPERM{r.f.Name() + ": WriteAt outside of a transaction"} + } + + return r.bitFiler.WriteAt(b, off) +} diff --git a/Godeps/_workspace/src/github.com/cznic/exp/lldb/xact_test.go b/Godeps/_workspace/src/github.com/cznic/exp/lldb/xact_test.go new file mode 100644 index 000000000..77b204d90 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/exp/lldb/xact_test.go @@ -0,0 +1,400 @@ +// Copyright 2014 The lldb Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lldb + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "math/rand" + "testing" + + "github.com/cznic/fileutil" + "github.com/cznic/mathutil" +) + +func (f *bitFiler) dump(w io.Writer) { + fmt.Fprintf(w, "bitFiler @ %p, size: %d(%#x)\n", f, f.size, f.size) + for k, v := range f.m { + fmt.Fprintf(w, "bitPage @ %p: pgI %d(%#x): %#v\n", v, k, k, *v) + } +} + +func filerBytes(f Filer) []byte { + sz, err := f.Size() + if err != nil { + panic(err) + } + + b := make([]byte, int(sz)) + n, err := f.ReadAt(b, 0) + if n != len(b) { + panic(fmt.Errorf("sz %d n %d err %v", sz, n, err)) + } + + return b +} + +func cmpFilerBytes(t *testing.T, fg, fe Filer) { + g, e := filerBytes(fg), filerBytes(fe) + if !bytes.Equal(g, e) { + t.Fatalf("Filer content doesn't match: got\n%sexp:\n%s", hex.Dump(g), hex.Dump(e)) + } +} + +func TestRollbackFiler0(t *testing.T) { + var r *RollbackFiler + f, g := NewMemFiler(), NewMemFiler() + + checkpoint := func(sz int64) (err error) { + return f.Truncate(sz) + } + + r, err := NewRollbackFiler(f, checkpoint, f) + if err != nil { + t.Fatal(err) + } + + if err = r.BeginUpdate(); err != nil { + t.Fatal(err) + } + + if err = r.EndUpdate(); err != nil { + t.Fatal(err) + } + + cmpFilerBytes(t, f, g) +} + +func TestRollbackFiler1(t *testing.T) { + const ( + N = 1e6 + O = 1234 + ) + + var r *RollbackFiler + f, g := NewMemFiler(), NewMemFiler() + + checkpoint := func(sz int64) (err error) { + return f.Truncate(sz) + } + + r, err := NewRollbackFiler(f, checkpoint, f) + if err != nil { + t.Fatal(err) + } + + if err = r.BeginUpdate(); err != nil { + t.Fatal(err) + } + + rng := rand.New(rand.NewSource(42)) + b := make([]byte, N) + for i := range b { + b[i] = byte(rng.Int()) + } + + if _, err = g.WriteAt(b, O); err != nil { + t.Fatal(err) + } + + if _, err = r.WriteAt(b, O); err != nil { + t.Fatal(err) + } + + b = filerBytes(f) + if n := len(b); n != 0 { + t.Fatal(n) + } + + if err = r.EndUpdate(); err != nil { + t.Fatal(err) + } + + cmpFilerBytes(t, f, g) +} + +func TestRollbackFiler2(t *testing.T) { + const ( + N = 1e6 + O = 1234 + ) + + var r *RollbackFiler + f, g := NewMemFiler(), NewMemFiler() + + checkpoint := func(sz int64) (err error) { + return f.Truncate(sz) + } + + r, err := NewRollbackFiler(f, checkpoint, f) + if err != nil { + t.Fatal(err) + } + + if err = r.BeginUpdate(); err != nil { + t.Fatal(err) + } + + rng := rand.New(rand.NewSource(42)) + b := make([]byte, N) + for i := range b { + b[i] = byte(rng.Int()) + } + + if _, err = r.WriteAt(b, O); err != nil { + t.Fatal(err) + } + + b = filerBytes(f) + if n := len(b); n != 0 { + t.Fatal(n) + } + + if err = r.Rollback(); err != nil { + t.Fatal(err) + } + + cmpFilerBytes(t, f, g) +} + +func rndBytes(rng *rand.Rand, n int) []byte { + r := make([]byte, n) + for i := range r { + r[i] = byte(rng.Int()) + } + return r +} + +func TestRollbackFiler3(t *testing.T) { + var r *RollbackFiler + f := NewMemFiler() + + checkpoint := func(sz int64) (err error) { + return f.Truncate(sz) + } + + r, err := NewRollbackFiler(f, checkpoint, f) + if err != nil { + t.Fatal(err) + } + + n, err := r.ReadAt([]byte{0}, 0) + if n != 0 || !fileutil.IsEOF(err) { + t.Fatal(n, err) + } + + n, err = r.ReadAt([]byte{0}, 1e6) + if n != 0 || !fileutil.IsEOF(err) { + t.Fatal(n, err) + } + + if err = r.BeginUpdate(); err != nil { // BeginUpdate: 0 -> 1 + t.Fatal(err) + } + + rng := rand.New(rand.NewSource(42)) + + buf := rndBytes(rng, 100) + if n, err := r.WriteAt(buf, 1e6); n != 100 || err != nil { + t.Fatal(err) + } + + buf = make([]byte, 100) + if n, err := r.ReadAt(buf, 1e6-200); n != 100 || err != nil { + t.Fatal(err) + } + + for i, v := range buf { + if v != 0 { + t.Fatal(i, v) + } + } + + if err := r.Truncate(1e5); err != nil { + t.Fatal(err) + } + + if err = r.BeginUpdate(); err != nil { // BeginUpdate: 1 -> 2 + t.Fatal(err) + } + + if n, err := r.ReadAt(buf, 1e6); n != 0 || err == nil { + t.Fatal(n, err) + } + + if err := r.Truncate(2e6); err != nil { + t.Fatal(err) + } + + if err = r.BeginUpdate(); err != nil { // BeginUpdate: 2 -> 3 + t.Fatal(err) + } + + if n, err := r.ReadAt(buf, 1e6); n == 0 || err != nil { + t.Fatal(n, err) + } + + for i, v := range buf { + if v != 0 { + t.Fatal(i, v) + } + } +} + +func TestRollbackFiler4(t *testing.T) { + const ( + maxSize = 1e6 + maxChange = maxSize/100 + 4 + maxChanges = 10 + maxNest = 3 + ) + + var r *RollbackFiler + f := NewMemFiler() + + checkpoint := func(sz int64) (err error) { + return f.Truncate(sz) + } + + r, err := NewRollbackFiler(f, checkpoint, f) + if err != nil { + t.Fatal(err) + } + + rng := rand.New(rand.NewSource(42)) + + ref := make([]byte, 2*maxSize) + for i := range ref { + ref[i] = byte(rng.Int()) + } + + var finalSize int + + var fn func(int, int, []byte) (int, []byte) + fn = func(nest, inSize int, in []byte) (outSize int, out []byte) { + defer func() { + for i := outSize; i < len(out); i++ { + out[i] = 0 + } + finalSize = mathutil.Max(finalSize, outSize) + }() + + out = make([]byte, len(in), 2*maxSize) + copy(out, in) + if err := r.BeginUpdate(); err != nil { + t.Fatal(err) + } + + for i := 0; i < maxChanges; i++ { + changeLen := rng.Intn(maxChange) + 4 + changeOff := rng.Intn(maxSize * 3 / 2) + b := make([]byte, changeLen) + for i := range b { + b[i] = byte(rng.Int()) + } + if n, err := r.WriteAt(b, int64(changeOff)); n != len(b) || err != nil { + t.Fatal(n, len(b), err) + } + } + + if err := r.Rollback(); err != nil { + t.Fatal(err) + } + + if err := r.BeginUpdate(); err != nil { + t.Fatal(err) + } + + for i := 0; i < maxChanges; i++ { + changeLen := rng.Intn(maxChange) + 4 + changeOff := rng.Intn(maxSize * 3 / 2) + b := make([]byte, changeLen) + for i := range b { + b[i] = byte(rng.Int()) + } + if n, err := r.WriteAt(b, int64(changeOff)); n != len(b) || err != nil { + t.Fatal(n, len(b), err) + } + copy(out[changeOff:], b) + copy(ref[changeOff:], b) + } + + newSize := rng.Intn(maxSize*3/2) + 4 + if nest == maxNest { + if err := r.EndUpdate(); err != nil { + t.Fatal(err) + } + + return newSize, out + } + + outSize, out = fn(nest+1, newSize, out) + if err := r.EndUpdate(); err != nil { + t.Fatal(err) + } + + return + } + + sz, result := fn(0, maxSize, ref) + if g, e := sz, finalSize; g != e { + t.Fatal(err) + } + + g, e := result[:sz], ref[:sz] + if !bytes.Equal(g, e) { + if len(g) == len(e) { + x := make([]byte, len(g)) + for i := range x { + if g[i] != e[i] { + x[i] = 'X' + } + } + //t.Logf("Data diff\n%s", hex.Dump(x)) + } + //t.Fatalf("Data don't match: got\n%sexp:\n%s", hex.Dump(g), hex.Dump(e)) + t.Fatalf("Data don't match") + } +} + +func BenchmarkRollbackFiler(b *testing.B) { + rng := rand.New(rand.NewSource(42)) + type t struct { + off int64 + b []byte + } + a := []t{} + for rem := b.N; rem > 0; { + off := rng.Int63() + n := mathutil.Min(rng.Intn(1e3)+1, rem) + a = append(a, t{off, rndBytes(rng, n)}) + rem -= n + } + + var r *RollbackFiler + f := NewMemFiler() + + checkpoint := func(sz int64) (err error) { + return f.Truncate(sz) + } + + r, err := NewRollbackFiler(f, checkpoint, f) + if err != nil { + b.Fatal(err) + } + + if err := r.BeginUpdate(); err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for _, v := range a { + if _, err := r.WriteAt(v.b, v.off); err != nil { + b.Fatal(err) + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/AUTHORS b/Godeps/_workspace/src/github.com/cznic/fileutil/AUTHORS new file mode 100644 index 000000000..288ea0b1e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/AUTHORS @@ -0,0 +1,14 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +CZ.NIC z.s.p.o. +Jan Mercl <0xjnml@gmail.com> +Aaron Bieber + diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/fileutil/CONTRIBUTORS new file mode 100644 index 000000000..223b21a30 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/CONTRIBUTORS @@ -0,0 +1,14 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Bill Thiede +Gary Burd +Jan Mercl <0xjnml@gmail.com> +Nick Owens +Tamás Gulácsi +Aaron Bieber diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/LICENSE b/Godeps/_workspace/src/github.com/cznic/fileutil/LICENSE new file mode 100644 index 000000000..50bbdd241 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The fileutil Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/Makefile b/Godeps/_workspace/src/github.com/cznic/fileutil/Makefile new file mode 100644 index 000000000..5849cc20a --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/Makefile @@ -0,0 +1,27 @@ +# Copyright (c) 2014 The fileutil authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all clean editor todo + +all: editor + go vet + golint . + go install + make todo + +editor: + go fmt + go test -i + go test + go build + +todo: + @grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* *.go || true + @grep -n TODO *.go || true + @grep -n BUG *.go || true + @grep -n println *.go || true + +clean: + @go clean + rm -f y.output diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/README b/Godeps/_workspace/src/github.com/cznic/fileutil/README new file mode 100644 index 000000000..f43d5f004 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/README @@ -0,0 +1,16 @@ +This is a goinstall-able mirror of modified code already published at: +http://git.nic.cz/redmine/projects/gofileutil/repository + +Packages in this repository: + +Install: $go get github.com/cznic/fileutil +Godocs: http://godoc.org/github.com/cznic/fileutil + +Install: $go get github.com/cznic/fileutil/storage +Godocs: http://godoc.org/github.com/cznic/fileutil/storage + +Install: $go get github.com/cznic/fileutil/falloc +Godocs: http://godoc.org/github.com/cznic/fileutil/falloc + +Install: $go get github.com/cznic/fileutil/hdb +Godocs: http://godoc.org/github.com/cznic/fileutil/hdb diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/all_test.go b/Godeps/_workspace/src/github.com/cznic/fileutil/all_test.go new file mode 100644 index 000000000..21a8fa948 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/all_test.go @@ -0,0 +1,39 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fileutil + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestTempFile(t *testing.T) { + f, err := TempFile("", "abc", "mno.xyz") + if err != nil { + t.Fatal(err) + } + + n := f.Name() + t.Log(n) + defer func() { + f.Close() + os.Remove(n) + }() + + base := filepath.Base(n) + if base == "abcmno.xyz" { + t.Fatal(base) + } + + if !strings.HasPrefix(base, "abc") { + t.Fatal(base) + } + + if !strings.HasSuffix(base, "mno.xyz") { + t.Fatal(base) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/LICENSE b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/LICENSE new file mode 100644 index 000000000..1e92e33dd --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of CZ.NIC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/README b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/README new file mode 100644 index 000000000..23313fc50 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/README @@ -0,0 +1,5 @@ +This is a goinstall-able mirror of modified code already published at: +https://git.nic.cz/redmine/projects/gofileutil/repository/show/falloc + +Install: $go get github.com/cznic/fileutil/falloc +Godocs: http://gopkgdoc.appspot.com/pkg/github.com/cznic/fileutil/falloc diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/all_test.go b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/all_test.go new file mode 100644 index 000000000..d70dcaff0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/all_test.go @@ -0,0 +1,3105 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package falloc + +import ( + "bytes" + "errors" + "flag" + "fmt" + "io/ioutil" + "log" + "math" + "os" + "path/filepath" + "runtime" + "testing" + "time" + + "github.com/cznic/fileutil" + "github.com/cznic/fileutil/storage" + "github.com/cznic/mathutil" +) + +var ( + blockFlag = flag.Uint("block", 256, "block size for some of the dev tests") + cacheTotalFlag = flag.Int64("cachemax", 1<<25, "cache total bytes") + cachedFlag = flag.Bool("cache", false, "enable caching store") + devFlag = flag.Bool("dev", false, "enable dev tests") + dropFlag = flag.Bool("drop", false, "drop system file cache for some of the dev tests before measurement") + fadviseFlag = flag.Bool("fadvise", false, "hint kernel about random file access") + nFlag = flag.Int("n", 1, "parameter for some of the dev tests") + probeFlag = flag.Bool("probe", false, "report store probe statistics") + optGo = flag.Int("go", 3, "GOMAXPROCS") +) + +func init() { + flag.Parse() + runtime.GOMAXPROCS(*optGo) +} + +func temp() (dir, name string) { + dir, err := ioutil.TempDir("", "test-falloc-") + if err != nil { + panic(err) + } + + name = filepath.Join(dir, "test.db") + return dir, name +} + +type balancedAcid struct { + storage.Accessor + nesting int +} + +func newBalancedAcid(store storage.Accessor) storage.Accessor { + return &balancedAcid{store, 0} +} + +func (b *balancedAcid) BeginUpdate() error { + if b.nesting < 0 { + return errors.New("BeginUpdate with nesting < 0") + } + + b.nesting++ + return nil +} + +func (b *balancedAcid) EndUpdate() error { + if b.nesting <= 0 { + return errors.New("EndUpdate with nesting <= 0") + } + + b.nesting-- + return nil +} + +func (b *balancedAcid) Close() error { + if b.nesting != 1 { + return fmt.Errorf("before Close(): nesting %d %p", b.nesting, b) + } + + if err := b.Accessor.Close(); err != nil { + return err + } + + if b.nesting != 1 { + return fmt.Errorf("after Close(): nesting %d", b.nesting) + } + + return nil +} + +func fopen(fn string) (f *File, err error) { + var store storage.Accessor + if store, err = storage.OpenFile(fn, os.O_RDWR, 0666); err != nil { + return + } + + var advise func(int64, int, bool) + if *fadviseFlag { + file := store.(*storage.FileAccessor).File + if err = fileutil.Fadvise(file, 0, 0, fileutil.POSIX_FADV_RANDOM); err != nil { + return + } + advise = func(off int64, len int, write bool) { + if err = fileutil.Fadvise(file, off, off+int64(len), fileutil.POSIX_FADV_DONTNEED); err != nil { + log.Fatal("advisor advise err", err) + } + } + } + + var prob *storage.Probe + if *probeFlag { + prob = storage.NewProbe(store, nil) + store = prob + } + if *cachedFlag { + if store, err = storage.NewCache(store, *cacheTotalFlag, advise); err != nil { + return + } + + if *probeFlag { + store = storage.NewProbe(store, prob) + } + } + f, err = Open(newBalancedAcid(store)) + return +} + +func fcreate(fn string) (f *File, err error) { + var store storage.Accessor + if store, err = storage.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666); err != nil { + return + } + + var advise func(int64, int, bool) + if *fadviseFlag { + file := store.(*storage.FileAccessor).File + if err = fileutil.Fadvise(file, 0, 0, fileutil.POSIX_FADV_RANDOM); err != nil { + return + } + advise = func(off int64, len int, write bool) { + if err = fileutil.Fadvise(file, off, off+int64(len), fileutil.POSIX_FADV_DONTNEED); err != nil { + log.Fatal("advisor advise err", err) + } + } + } + + var prob *storage.Probe + if *probeFlag { + prob = storage.NewProbe(store, nil) + store = prob + } + if *cachedFlag { + if store, err = storage.NewCache(store, *cacheTotalFlag, advise); err != nil { + return + } + + if *probeFlag { + store = storage.NewProbe(store, prob) + } + } + f, err = New(newBalancedAcid(store)) + return +} + +func probed(t *testing.T, f *File) { + if f == nil { + return + } + + dump := func(p *storage.Probe) { + t.Logf("OpsRd %d OpsWr %d BytesRd %d(avg %.1f) BytesWr %d(avg %.1f) SectorsRd %d(%d, +%d, x%.2f) SectorsWr %d(%d, +%d, x%.2f)", + p.OpsRd, p.OpsWr, + p.BytesRd, float64(p.BytesRd)/float64(p.OpsRd), + p.BytesWr, float64(p.BytesWr)/float64(p.OpsWr), + p.SectorsRd, + p.SectorsRd<<9, + p.SectorsRd<<9-p.BytesRd, + float64(p.SectorsRd<<9)/float64(p.BytesRd), + p.SectorsWr, + p.SectorsWr<<9, + p.SectorsWr<<9-p.BytesWr, + float64(p.SectorsWr<<9)/float64(p.BytesWr), + ) + } + + if ph, ok := f.Accessor().(*storage.Probe); ok { + dump(ph) + if c, ok := ph.Accessor.(*storage.Cache); ok { + if pl, ok := c.Accessor().(*storage.Probe); ok { + dump(pl) + } + } + } +} + +func (f *File) audit() (usedblocks, totalblocks int64, err error) { + defer func() { + if e := recover(); e != nil { + err = e.(error) + } + }() + + fi, err := f.f.Stat() + if err != nil { + panic(err) + } + + freemap := map[int64]int64{} + fp := int64(0) + buf := make([]byte, 22) + freeblocks := int64(0) + + // linear scan + for fp < fi.Size() { + totalblocks++ + typ, size := f.getInfo(fp >> 4) + f.read(buf[:1], fp+size<<4-1) + last := buf[0] + switch { + default: + panic("internal error") + case typ == 0: + if last != 0 { + panic(fmt.Errorf("@%#x used empty, last @%#x: %#x != 0", fp, fp+size<<4-1, last)) + } + case typ >= 0x1 && typ <= 0xed: + if last >= 0xfe { + panic(fmt.Errorf("@%#x used short, last @%#x: %#x > 0xfe", fp, fp+size<<4-1, last)) + } + case typ >= 0xee && typ <= 0xfb: + if last > 1 { + panic(fmt.Errorf("@%#x used esc short, last @%#x: %#x > 1", fp, fp+size<<4-1, last)) + } + case typ == 0xfc: + f.read(buf[:2], fp+1) + switch n := int(buf[0])<<8 + int(buf[1]); { + default: + panic(fmt.Errorf("@%#x used long, illegal content length %#x < 0xee(238)", fp, n)) + case n >= 0xee && n <= 0xf0f0: + if last >= 0xfe { + panic(fmt.Errorf("@%#x used long, last @%#x: %#x > 0xfe", fp, fp+size<<4-1, last)) + } + case n >= 0xf0f1 && n <= 0xffff: + if last > 1 { + panic(fmt.Errorf("@%#x used esc long, last @%#x: %#x > 1", fp, fp+size<<4-1, last)) + } + } + case typ == 0xfd: + if last != 0 { + panic(fmt.Errorf("@%#x reloc, last @%#x: %#x != 0", fp, fp+size<<4-1, last)) + } + + var target int64 + f.read(buf[:7], fp+1) + (*Handle)(&target).Get(buf) + if target >= f.atoms { + panic(fmt.Errorf("@%#x illegal reloc, target %#x > f.atoms(%#x)", fp, target, f.atoms)) + } + + ttyp, _ := f.getInfo(target) + if ttyp >= 0xfe { + panic(fmt.Errorf("@%#x reloc, points to unused @%#x", fp, target)) + } + + if ttyp == 0xfd { + panic(fmt.Errorf("@%#x reloc, points to reloc @%#x", fp, target)) + } + case typ == 0xfe: + if size < 2 { + panic(fmt.Errorf("@%#x illegal free block, atoms %d < 2", fp, size)) + } + + if fp>>4 < f.canfree { + panic(fmt.Errorf("@%#x illegal free block @ < f.canfree", fp)) + } + + f.read(buf[:22], fp) + var prev, next, sz int64 + (*Handle)(&prev).Get(buf[1:]) + (*Handle)(&next).Get(buf[8:]) + f.checkPrevNext(fp, prev, next) + f.read(buf[:7], fp+size<<4-8) + (*Handle)(&sz).Get(buf) + if sz != size { + panic(fmt.Errorf("@%#x mismatch size, %d != %d", fp, sz, size)) + } + + if last != 0xfe { + panic(fmt.Errorf("@%#x free atom, last @%#x: %#x != 0xff", fp, fp+size<<4-1, last)) + } + freemap[fp>>4] = size + freeblocks++ + case typ == 0xff: + f.read(buf[:14], fp+1) + var prev, next int64 + (*Handle)(&prev).Get(buf) + (*Handle)(&next).Get(buf[7:]) + f.checkPrevNext(fp, prev, next) + if last != 0xff { + panic(fmt.Errorf("@%#x free atom, last @%#x: %#x != 0xff", fp, fp+size<<4-1, last)) + } + freemap[fp>>4] = size + freeblocks++ + } + fp += size << 4 + } + usedblocks = totalblocks - freeblocks + + // check free table + for size := len(f.freetab) - 1; size > 0; size-- { + var prev, next, fprev int64 + this := f.freetab[size] + for this != 0 { + sz, ok := freemap[this] + if !ok { + panic(fmt.Errorf("bad freetab[%d] item @%#x", size, this)) + } + + delete(freemap, this) + + if sz < int64(size) { + panic(fmt.Errorf("bad freetab[%d] item size @%#x %d", size, this, sz)) + } + + if sz == 1 { + f.read(buf[:15], this<<4) + (*Handle)(&fprev).Get(buf[1:]) + if fprev != prev { + panic(fmt.Errorf("bad fprev %#x, exp %#x", fprev, prev)) + } + + (*Handle)(&next).Get(buf[8:]) + } else { + f.read(buf, this<<4) + (*Handle)(&fprev).Get(buf[1:]) + if fprev != prev { + panic(fmt.Errorf("bad fprev %#x, exp %#x", fprev, prev)) + } + var fsz int64 + (*Handle)(&fsz).Get(buf[15:]) + if fsz != sz { + panic(fmt.Errorf("bad fsz %d @%#x, exp %#x", fsz, this<<4, sz)) + } + + (*Handle)(&next).Get(buf[8:]) + } + + prev, this = this, next + } + } + + if n := len(freemap); n != 0 { + for h, s := range freemap { + panic(fmt.Errorf("%d lost free blocks in freemap, e.g. %d free atoms @%#x", n, s, h)) + } + } + + return + +} + +func (f *File) checkPrevNext(fp, prev, next int64) { + if prev != 0 && prev < f.canfree { + panic(fmt.Errorf("@%#x illegal free atom, prev %#x < f.canfree(%#x)", fp, prev, f.canfree)) + } + + if prev >= f.atoms { + panic(fmt.Errorf("@%#x illegal free atom, prev %#x > f.atoms", fp, prev)) + } + + if next != 0 && next < f.canfree { + panic(fmt.Errorf("@%#x illegal free atom, next %#x < f.canfree(%#x)", fp, next, f.canfree)) + } + + if next >= f.atoms { + panic(fmt.Errorf("@%#x illegal free atom, next %#x > f.atoms", fp, next)) + } +} + +func reaudit(t *testing.T, f *File, fn string) (of *File) { + var err error + if _, _, err := f.audit(); err != nil { + t.Fatal(err) + } + + if err := f.Close(); err != nil { + t.Fatal(err) + } + + f = nil + runtime.GC() + if of, err = fopen(fn); err != nil { + t.Fatal(err) + } + + if _, _, err := of.audit(); err != nil { + t.Fatal(err) + } + + return +} + +func TestCreate(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + err := os.Remove(name) + if err != nil { + t.Fatal(err) + } + }() + + f.Accessor().Sync() + probed(t, f) + if err = f.Close(); err != nil { + t.Log(f.f.(*balancedAcid).nesting) + t.Fatal(err) + } + + b, err := ioutil.ReadFile(name) + if err != nil { + t.Fatal(err) + } + + x := b[:16] + if !bytes.Equal(x, hdr) { + t.Fatalf("\n% x\n% x", x, hdr) + } + + x = b[16:32] + if !bytes.Equal(x, empty) { + t.Fatalf("\n% x\n% x", x, hdr) + } +} + +func TestOpen(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + probed(t, f) + ec := f.Close() + er := os.Remove(name) + if ec != nil { + t.Fatal(ec) + } + + if er != nil { + t.Fatal(er) + } + }() + + if err := f.Close(); err != nil { + t.Fatal(err) + } + + if f, err = fopen(name); err != nil { + t.Fatal(err) + } + + for i, p := range f.freetab { + if p != 0 { + t.Fatal(i+1, p) + } + } +} + +func alloc(f *File, b []byte) (y int64) { + if h, err := f.Alloc(b); err != nil { + panic(err) + } else { + y = int64(h) + } + return +} + +func realloc(f *File, atom int64, b []byte, keepHandle bool) (y int64) { + if h, err := f.Realloc(Handle(atom), b, keepHandle); err != nil { + panic(err) + } else { + y = int64(h) + } + return +} + +func testContentEncodingDecoding(t *testing.T, min, max int) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + ec := f.Close() + er := os.Remove(name) + if ec != nil { + t.Fatal(ec) + } + + if er != nil { + t.Fatal(er) + } + }() + + b := make([]byte, max) + r, err := mathutil.NewFC32(math.MinInt32, math.MaxInt32, true) + if err != nil { + t.Fatal(err) + } + + blocks := int64(3) + a := make([]int64, 0, 4*(max-min+1)) + for cl := min; cl <= max; cl++ { + src := b[:cl] + for i := range src { + b[i] = byte(r.Next()) + } + a = append(a, alloc(f, src)) + blocks++ + if cl == 0 { + continue + } + + for i := range src { + b[i] = byte(r.Next()) + } + src[cl-1] = 0xfd + a = append(a, alloc(f, src)) + blocks++ + for i := range src { + b[i] = byte(r.Next()) + } + src[cl-1] = 0xfe + a = append(a, alloc(f, src)) + blocks++ + for i := range src { + b[i] = byte(r.Next()) + } + src[cl-1] = 0xff + a = append(a, alloc(f, src)) + blocks++ + } + + f.Accessor().Sync() + probed(t, f) + if err := f.Close(); err != nil { + t.Fatal(err) + } + + f = nil + runtime.GC() + if f, err = fopen(name); err != nil { + t.Fatal(err) + } + + r.Seek(0) + ai := 0 + for cl := min; cl <= max; cl++ { + h := a[ai] + ai++ + src := b[:cl] + for i := range src { + b[i] = byte(r.Next()) + } + got, _ := f.readUsed(h) + if !bytes.Equal(src, got) { + t.Fatalf("cl %d atom %#x\nexp % x\ngot % x", cl, h, src, got) + } + if cl == 0 { + continue + } + + for i := range src { + b[i] = byte(r.Next()) + } + src[cl-1] = 0xfd + h = a[ai] + ai++ + got, _ = f.readUsed(h) + if !bytes.Equal(src, got) { + t.Fatalf("cl %d atom %#x\nexp % x\ngot % x", cl, h, src, got) + } + + for i := range src { + b[i] = byte(r.Next()) + } + src[cl-1] = 0xfe + h = a[ai] + ai++ + got, _ = f.readUsed(h) + if !bytes.Equal(src, got) { + t.Fatalf("cl %d atom %#x\nexp % x\ngot % x", cl, h, src, got) + } + + for i := range src { + b[i] = byte(r.Next()) + } + src[cl-1] = 0xff + h = a[ai] + ai++ + got, _ = f.readUsed(h) + if !bytes.Equal(src, got) { + t.Fatalf("cl %d atom %#x\nexp % x\ngot % x", cl, h, src, got) + } + } + + auditblocks, _, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if auditblocks != blocks { + t.Fatal(auditblocks, blocks) + } + + if f = reaudit(t, f, name); err != nil { + t.Fatal(err) + } +} + +func TestContentEncodingDecoding(t *testing.T) { + testContentEncodingDecoding(t, 0, 1024) + testContentEncodingDecoding(t, 61680-17, 61680) +} + +type freeItem struct { + size int64 + head int64 +} + +func (f *File) reportFree() (report []freeItem) { + for size, head := range f.freetab { + if size != 0 && head != 0 { + report = append(report, freeItem{int64(size), head}) + } + } + return +} + +func free(f *File, h int64) { + if err := f.Free(Handle(h)); err != nil { + panic(err) + } +} + +func testFreeTail(t *testing.T, b []byte) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + ec := f.Close() + er := os.Remove(name) + if ec != nil { + t.Fatal(ec) + } + + if er != nil { + t.Fatal(er) + } + }() + + fs0 := f.atoms + used0, total0, err := f.audit() + if err != nil { + panic(err) + } + + if used0 != total0 { + t.Fatal(used0, total0) + } + + handle := alloc(f, b) + free(f, handle) + if fs1 := f.atoms; fs1 != fs0 { + t.Fatal(fs1, fs0) + } + + if rep := f.reportFree(); len(rep) != 0 { + t.Fatal(rep) + } + + if err := f.Close(); err != nil { + t.Fatal(err) + } + + f = nil + runtime.GC() + if f, err = fopen(name); err != nil { + t.Fatal(err) + } + + used, total, err := f.audit() + if err != nil { + panic(err) + } + + if used != used0 { + t.Fatal(used, used0) + } + + if total != total0 { + t.Fatal(total, total0) + } +} + +func TestFreeTail(t *testing.T) { + b := make([]byte, 61680) + for n := 0; n <= 253+16; n++ { + data := b[:n] + testFreeTail(t, data) + if n == 0 { + continue + } + + data[n-1] = 0xff + testFreeTail(t, data) + data[n-1] = 0 + } + + for n := 61680 - 16; n <= 61680; n++ { + data := b[:n] + testFreeTail(t, data) + data[n-1] = 0xff + testFreeTail(t, data) + data[n-1] = 0 + } +} + +func testFreeTail2(t *testing.T, b []byte) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + ec := f.Close() + er := os.Remove(name) + if ec != nil { + t.Fatal(ec) + } + + if er != nil { + t.Fatal(er) + } + }() + + fs0 := f.atoms + used0, total0, err := f.audit() + if err != nil { + panic(err) + } + + if used0 != total0 { + t.Fatal(used0, total0) + } + + handle := alloc(f, b) + handle2 := alloc(f, b) + free(f, handle) + free(f, handle2) + if fs1 := f.atoms; fs1 != fs0 { + t.Fatal(fs1, fs0) + } + + if rep := f.reportFree(); len(rep) != 0 { + t.Fatal(rep) + } + + if err := f.Close(); err != nil { + t.Fatal(err) + } + + f = nil + runtime.GC() + if f, err = fopen(name); err != nil { + t.Fatal(err) + } + + used, total, err := f.audit() + if err != nil { + panic(err) + } + + if used != used0 { + t.Fatal(used, used0) + } + + if total != total0 { + t.Fatal(total, total0) + } +} + +func TestFreeTail2(t *testing.T) { + b := make([]byte, 61680) + for n := 0; n <= 253+16; n++ { + data := b[:n] + testFreeTail2(t, data) + if n == 0 { + continue + } + + data[n-1] = 0xff + testFreeTail2(t, data) + data[n-1] = 0 + } + + for n := 61680 - 16; n <= 61680; n++ { + data := b[:n] + testFreeTail2(t, data) + data[n-1] = 0xff + testFreeTail2(t, data) + data[n-1] = 0 + } +} + +func testFreeIsolated(t *testing.T, b []byte) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + ec := f.Close() + er := os.Remove(name) + if ec != nil { + t.Fatal(ec) + } + + if er != nil { + t.Fatal(er) + } + }() + + rqAtoms := rq2Atoms(len(b)) + left := alloc(f, nil) + handle := alloc(f, b) + right := alloc(f, nil) + + fs0 := f.atoms + used0, total0, err := f.audit() + if err != nil { + panic(err) + } + + if used0 != total0 { + t.Fatal(used0, total0) + } + + free(f, handle) + if fs1 := f.atoms; fs1 != fs0 { + t.Fatal(fs1, fs0) + } + + rep := f.reportFree() + if len(rep) != 1 { + t.Fatal(rep) + } + + if x := rep[0]; x.size != rqAtoms || x.head != handle { + t.Fatal(x) + } + + used, total, err := f.audit() + if err != nil { + panic(err) + } + + if n, free := f.getSize(left); n != 1 || free { + t.Fatal(n, free) + } + + if n, free := f.getSize(right); n != 1 || free { + t.Fatal(n, free) + } + + if used != used0-1 { + t.Fatal(used, used0) + } + + if total != total0 { + t.Fatal(total, total0) + } + + if free := total - used; free != 1 { + t.Fatal(free) + } + + // verify persisted file correct + if err := f.Close(); err != nil { + t.Fatal(err) + } + + f = nil + runtime.GC() + if f, err = fopen(name); err != nil { + t.Fatal(err) + } + + if fs1 := f.atoms; fs1 != fs0 { + t.Fatal(fs1, fs0) + } + + rep = f.reportFree() + if len(rep) != 1 { + t.Fatal(rep) + } + + if x := rep[0]; x.size != rqAtoms || x.head != handle { + t.Fatal(x) + } + + used, total, err = f.audit() + if err != nil { + panic(err) + } + + if n, free := f.getSize(left); n != 1 || free { + t.Fatal(n, free) + } + + if n, free := f.getSize(right); n != 1 || free { + t.Fatal(n, free) + } + + if used != used0-1 { + t.Fatal(used, used0) + } + + if total != total0 { + t.Fatal(total, total0) + } + + if free := total - used; free != 1 { + t.Fatal(free) + } + +} + +func TestFreeIsolated(t *testing.T) { + b := make([]byte, 61680) + for n := 0; n <= 253+16; n++ { + data := b[:n] + testFreeIsolated(t, data) + } + + for n := 61680 - 16; n <= 61680; n++ { + data := b[:n] + testFreeIsolated(t, data) + } +} + +func testFreeBlockList(t *testing.T, a, b int) { + var h [2]int64 + + t.Log(a, b) + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + alloc(f, nil) + h[0] = alloc(f, nil) + alloc(f, nil) + h[1] = alloc(f, nil) + alloc(f, nil) + + if err := f.Close(); err != nil { + t.Fatal(err) + } + + f = nil + runtime.GC() + if f, err = fopen(name); err != nil { + t.Fatal(err) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if used-used0 != 5 || total-total0 != 5 || used != total { + t.Fatal(used0, total0, used, total) + } + + free(f, h[a]) + free(f, h[b]) + + used, total, err = f.audit() + if err != nil { + t.Fatal(err) + } + + if used-used0 != 3 || total-total0 != 5 || total-used != 2 { + t.Fatal(used0, total0, used, total) + } + + if err := f.Close(); err != nil { + t.Fatal(err) + } + + f = nil + runtime.GC() + if f, err = fopen(name); err != nil { + t.Fatal(err) + } + + used, total, err = f.audit() + if err != nil { + t.Fatal(err) + } + + if used-used0 != 3 || total-total0 != 5 || total-used != 2 { + t.Fatal(used0, total0, used, total) + } +} + +func TestFreeBlockList(t *testing.T) { + testFreeBlockList(t, 0, 1) + testFreeBlockList(t, 1, 0) +} + +func testFreeBlockList2(t *testing.T, a, b, c int) { + var h [3]int64 + + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + alloc(f, nil) + h[0] = alloc(f, nil) + alloc(f, nil) + h[1] = alloc(f, nil) + alloc(f, nil) + h[2] = alloc(f, nil) + alloc(f, nil) + + if err := f.Close(); err != nil { + t.Fatal(err) + } + + f = nil + runtime.GC() + if f, err = fopen(name); err != nil { + t.Fatal(err) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if used-used0 != 7 || total-total0 != 7 || used != total { + t.Fatal(used0, total0, used, total) + } + + free(f, h[a]) + free(f, h[b]) + free(f, h[c]) + + used, total, err = f.audit() + if err != nil { + t.Fatal(err) + } + + if used-used0 != 4 || total-total0 != 7 || total-used != 3 { + t.Fatal(used0, total0, used, total) + } + + if err := f.Close(); err != nil { + t.Fatal(err) + } + + f = nil + runtime.GC() + if f, err = fopen(name); err != nil { + t.Fatal(err) + } + + used, total, err = f.audit() + if err != nil { + t.Fatal(err) + } + + if used-used0 != 4 || total-total0 != 7 || total-used != 3 { + t.Fatal(used0, total0, used, total) + } +} + +func TestFreeBlockList2(t *testing.T) { + testFreeBlockList2(t, 0, 1, 2) + testFreeBlockList2(t, 0, 2, 1) + testFreeBlockList2(t, 1, 0, 2) + testFreeBlockList2(t, 1, 2, 0) + testFreeBlockList2(t, 2, 0, 1) + testFreeBlockList2(t, 2, 1, 0) +} + +var crng *mathutil.FC32 + +func init() { + var err error + if crng, err = mathutil.NewFC32(0, math.MaxInt32, true); err != nil { + panic(err) + } +} + +func content(b []byte, h int64) (c []byte) { + crng.Seed(h) + crng.Seek(0) + c = b[:crng.Next()%61681] + for i := range c { + c[i] = byte(crng.Next()) + } + return +} + +func testFreeBlockList3(t *testing.T, n, mod int) { + rng, err := mathutil.NewFC32(0, n-1, true) + if err != nil { + t.Fatal(err) + } + + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + ha := make([]int64, n) + b := make([]byte, 61680) + for i := range ha { + h := f.atoms + ha[i] = h + c := content(b, h) + if alloc(f, c) != h { + t.Fatal(h) + } + } + f = reaudit(t, f, name) + del := map[int64]bool{} + for _ = range ha { + i := rng.Next() + if i%mod != 0 { + h := ha[i] + free(f, h) + del[h] = true + } + } + f = reaudit(t, f, name) + for _, h := range ha { + if !del[h] { + exp := content(b, h) + got, _ := f.readUsed(h) + if !bytes.Equal(exp, got) { + t.Fatal(len(got), len(exp)) + } + } + } +} + +func TestFreeBlockList3(t *testing.T) { + testFreeBlockList3(t, 111, 1) + testFreeBlockList3(t, 151, 2) + testFreeBlockList3(t, 170, 3) + testFreeBlockList3(t, 170, 4) +} + +func TestRealloc1(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, nil) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + exp := c[:15] + if handle := realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != 0 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc1Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, nil) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + exp := c[:15] + if handle := realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != 0 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc2(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, c[:31]) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + exp := c[:15] + if handle := realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != 1 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc2Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, c[:31]) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + exp := c[:15] + if handle := realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != 1 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc3(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, nil) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + exp := c[:31] + var handle int64 + if handle = realloc(f, h10, exp, false); handle == h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != 1 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc3Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, nil) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + exp := c[:31] + var handle int64 + if handle = realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 1 || diftotal != 1 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc4Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, c[:31]) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + exp := c[:47] + var handle int64 + if handle = realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 1 || diftotal != 2 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc5(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, nil) + h15 := alloc(f, nil) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + free(f, h15) + exp := c[:31] + var handle int64 + if handle = realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != -1 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc5Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, nil) + h15 := alloc(f, nil) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + free(f, h15) + exp := c[:31] + var handle int64 + if handle = realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != -1 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc6(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, nil) + h15 := alloc(f, c[:31]) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + free(f, h15) + exp := c[:31] + var handle int64 + if handle = realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != 0 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRealloc6Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + h10 := alloc(f, nil) + h15 := alloc(f, c[:31]) + h20 := alloc(f, nil) + + used0, total0, err := f.audit() + if err != nil { + t.Fatal(err) + } + + free(f, h15) + exp := c[:31] + var handle int64 + if handle = realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(handle); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != 0 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc1(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + h20 := alloc(f, nil) + var handle int64 + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + used0, total0, err := f.audit() // c+3, c+3 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:15] + if handle = realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() // c+2, c+2 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != -1 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc1Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + h20 := alloc(f, nil) + var handle int64 + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + used0, total0, err := f.audit() // c+3, c+3 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:15] + if handle = realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + if got, _ := f.readUsed(h20); len(got) != 0 { + t.Fatal(len(got), 0) + } + + used, total, err := f.audit() // c+2, c+2 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != -1 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc2(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + h20 := alloc(f, nil) + var handle int64 + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + free(f, h20) + + used0, total0, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:31] + if handle = realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+1, c+1 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != -2 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc2Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + h20 := alloc(f, nil) + var handle int64 + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + free(f, h20) + + used0, total0, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:31] + if handle = realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+1, c+1 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != -2 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc3(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + h20 := alloc(f, b[:31]) + var handle int64 + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + free(f, h20) + + used0, total0, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:31] + if handle = realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+1, c+1 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != -2 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc3Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + h20 := alloc(f, b[:31]) + var handle int64 + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + free(f, h20) + + used0, total0, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:31] + if handle = realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+1, c+1 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != -2 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc4(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + _ = alloc(f, nil) + var handle int64 + if handle = realloc(f, h10, b[:47], true); handle != h10 { + t.Fatal(handle, h10) + } + + _ = alloc(f, nil) + + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + used0, total0, err := f.audit() // c+4, c+5 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:47] + if handle = realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+4, c+4 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != -1 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc4Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + _ = alloc(f, nil) + var handle int64 + if handle = realloc(f, h10, b[:47], true); handle != h10 { + t.Fatal(handle, h10) + } + + _ = alloc(f, nil) + + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + used0, total0, err := f.audit() // c+4, c+5 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:47] + if handle = realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+4, c+4 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != -1 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc5(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + _ = alloc(f, nil) + var handle int64 + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + _ = alloc(f, nil) + + used0, total0, err := f.audit() // c+4, c+4 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:47] + if handle = realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+4, c+5 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != 1 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc5Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, nil) + _ = alloc(f, nil) + var handle int64 + if handle = realloc(f, h10, b[:31], true); handle != h10 { + t.Fatal(handle, h10) + } + + _ = alloc(f, nil) + + used0, total0, err := f.audit() // c+4, c+4 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:47] + if handle = realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+4, c+5 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != 1 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc6(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, b[:31]) + h20 := alloc(f, nil) + _ = alloc(f, nil) + free(f, h20) + + used0, total0, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:15] + if handle := realloc(f, h10, exp, false); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != 0 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestRelocRealloc6Keep(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + + h10 := alloc(f, b[:31]) + h20 := alloc(f, nil) + _ = alloc(f, nil) + free(f, h20) + + used0, total0, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + c := content(b, 10) + exp := c[:15] + if handle := realloc(f, h10, exp, true); handle != h10 { + t.Fatal(handle, h10) + } + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, exp) { + t.Fatal(len(got), len(exp)) + } + + used, total, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 0 || diftotal != 0 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestFreespaceReuse(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + c10 := c[0 : 0+15] + c20 := c[16:63] + c50 := c[64 : 64+15] + h10 := alloc(f, c10) + h201 := alloc(f, nil) + h202 := alloc(f, nil) + h203 := alloc(f, nil) + h50 := alloc(f, c50) + free(f, h201) + free(f, h202) + free(f, h203) + used0, total0, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + h20 := alloc(f, c20) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, c10) { + t.Fatal() + } + + if got, _ := f.readUsed(h20); !bytes.Equal(got, c20) { + t.Fatal() + } + + if got, _ := f.readUsed(h50); !bytes.Equal(got, c50) { + t.Fatal() + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, c10) { + t.Fatal() + } + + if got, _ := f.readUsed(h20); !bytes.Equal(got, c20) { + t.Fatal() + } + + if got, _ := f.readUsed(h50); !bytes.Equal(got, c50) { + t.Fatal() + } + + used, total, err := f.audit() // c+3, c+3 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 1 || diftotal != 0 || free != 0 { + t.Fatal(difused, diftotal, free) + } +} + +func TestFreespaceReuse2(t *testing.T) { + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + c := content(b, 10) + + c10 := c[0 : 0+15] + c20 := c[16:47] + c50 := c[64 : 64+15] + h10 := alloc(f, c10) + h201 := alloc(f, nil) + h202 := alloc(f, nil) + h203 := alloc(f, nil) + h50 := alloc(f, c50) + free(f, h201) + free(f, h202) + free(f, h203) + used0, total0, err := f.audit() // c+2, c+3 + if err != nil { + t.Fatal(err) + } + + h20 := alloc(f, c20) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, c10) { + t.Fatal() + } + + if got, _ := f.readUsed(h20); !bytes.Equal(got, c20) { + t.Fatal() + } + + if got, _ := f.readUsed(h50); !bytes.Equal(got, c50) { + t.Fatal() + } + + f = reaudit(t, f, name) + + if got, _ := f.readUsed(h10); !bytes.Equal(got, c10) { + t.Fatal() + } + + if got, _ := f.readUsed(h20); !bytes.Equal(got, c20) { + t.Fatal() + } + + if got, _ := f.readUsed(h50); !bytes.Equal(got, c50) { + t.Fatal() + } + + used, total, err := f.audit() // c+3, c+4 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != 1 || diftotal != 1 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func testBug1(t *testing.T, swap bool) { + // Free lists table item for size 3856 points to list of free blocks + // NOT of size 3856 but at least 3856. + + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + _ = alloc(f, nil) + b := make([]byte, 61680) + f1 := alloc(f, b) + f2 := alloc(f, b) + _ = alloc(f, nil) + + used0, total0, err := f.audit() // c+4, c+4 + if err != nil { + t.Fatal(err) + } + + if swap { + f1, f2 = f2, f1 + } + free(f, f1) + free(f, f2) + _ = alloc(f, nil) + + f = reaudit(t, f, name) + + used, total, err := f.audit() // c+3, c+4 + if err != nil { + t.Fatal(err) + } + + if difused, diftotal, free := used-used0, total-total0, total-used; difused != -1 || diftotal != 0 || free != 1 { + t.Fatal(difused, diftotal, free) + } +} + +func TestBug1(t *testing.T) { + testBug1(t, false) + testBug1(t, true) +} + +func TestMix(t *testing.T) { + if testing.Short() { + t.Log("skipped") + return + } + + const ( + n = 1 << 10 + ) + + if testing.Short() { + t.Log("skipped") + return + } + + t.Log(n) + dir, name := temp() + defer os.RemoveAll(dir) + + f, err := fcreate(name) + if err != nil { + t.Fatal(err) + } + + defer func() { + if f != nil { + if err := f.Close(); err != nil { + t.Fatal(err) + } + } + + f = nil + runtime.GC() + os.Remove(name) + }() + + b := make([]byte, 61680) + rng, err := mathutil.NewFC32(0, n-1, true) + if err != nil { + t.Fatal(err) + } + + ha := make([]int64, n) + payload := 0 + + t0 := time.Now() + // Alloc n block with upper half of content + for _ = range ha { + r := rng.Next() + c := content(b, int64(r)) + c = c[len(c)/2:] + ha[r] = alloc(f, c) + payload += len(c) + } + dt := float64(time.Now().Sub(t0)) / 1e9 + t.Logf("write time A %.3g", dt) + + // verify + f = reaudit(t, f, name) + t.Logf("size A %d for %d bytes (fill factor %3.1f%%)", f.atoms<<4, payload, 100*float64(payload)/float64(f.atoms<<4)) + t0 = time.Now() + for _ = range ha { + r := rng.Next() + c := content(b, int64(r)) + c = c[len(c)/2:] + if got, _ := f.readUsed(ha[r]); !bytes.Equal(got, c) { + t.Fatal() + } + } + dt = float64(time.Now().Sub(t0)) / 1e9 + t.Logf("read time A %.3g", dt) + // free half of the blocks + t0 = time.Now() + for i := 0; i < n/2; i++ { + free(f, ha[i]) + ha[i] = 0 + } + dt = float64(time.Now().Sub(t0)) / 1e9 + t.Logf("free time A %.3g", dt) + + // verify + f = reaudit(t, f, name) + t.Logf("size B %d (freeing half of the blocks)", f.atoms<<4) + t0 = time.Now() + for _ = range ha { + r := rng.Next() + h := ha[r] + if h == 0 { + continue + } + + c := content(b, int64(r)) + c = c[len(c)/2:] + if got, _ := f.readUsed(h); !bytes.Equal(got, c) { + t.Fatal() + } + } + dt = float64(time.Now().Sub(t0)) / 1e9 + t.Logf("read time B %.3g", dt) + + // reloc extend + t0 = time.Now() + for _ = range ha { + r := rng.Next() + h := ha[r] + if h == 0 { + continue + } + + c := content(b, int64(r)) + //f = reaudit(t, f, name) + if h2 := realloc(f, h, c, true); h2 != h { + t.Fatal() + } + } + dt = float64(time.Now().Sub(t0)) / 1e9 + t.Logf("realoc time B %.3g", dt) + + // verify + f = reaudit(t, f, name) + t.Logf("size C %d for %d bytes (reallocated all used blocks to double size, fill factor %3.1f%%", f.atoms<<4, payload, 100*float64(payload)/float64(f.atoms<<4)) + + t0 = time.Now() + for _ = range ha { + r := rng.Next() + h := ha[r] + if h == 0 { + continue + } + + c := content(b, int64(r)) + if got, _ := f.readUsed(ha[r]); !bytes.Equal(got, c) { + t.Fatal() + } + } + dt = float64(time.Now().Sub(t0)) / 1e9 + t.Logf("read time C %.3g", dt) +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/docs.go b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/docs.go new file mode 100644 index 000000000..21772fcd0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/docs.go @@ -0,0 +1,251 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +/* + +WIP: Package falloc provides allocation/deallocation of space within a +file/store (WIP, unstable API). + +Overall structure: + File == n blocks. + Block == n atoms. + Atom == 16 bytes. + +x6..x0 == least significant 7 bytes of a 64 bit integer, highest (7th) byte is +0 and is not stored in the file. + +Block first byte + +Aka block type tag. + +------------------------------------------------------------------------------ + +0xFF: Free atom (free block of size 1). + +------++---------++---------++------+ + | 0 || 1...7 || 8...14 || 15 | + +------++---------++---------++------+ + | 0xFF || p6...p0 || n6...n0 || 0xFF | + +------++---------++---------++------+ + +Link to the previous free block (atom addressed) is p6...p0, next dtto in +n6...n0. Doubly linked lists of "compatible" free blocks allows for free space +reclaiming and merging. "Compatible" == of size at least some K. Heads of all +such lists are organized per K or intervals of Ks elsewhere. + +------------------------------------------------------------------------------ + +0xFE: Free block, size == s6...s0 atoms. + +------++---------++---------++---------++-- + | +0 || 1...7 || 8...14 || 15...21 || 22...16*size-1 + +------++---------++---------++---------++-- + | 0xFE || p6...p0 || n6...n0 || s6...s0 || ... + +------++---------++---------++---------++-- + +Prev and next links as in the 0xFF first byte case. End of this block - see +"Block last byte": 0xFE bellow. Data between == undefined. + +------------------------------------------------------------------------------ + +0xFD: Relocated block. + +------++---------++-----------++------+ + | 0 || 1...7 || 8...14 || 15 | + +------++---------++-----------++------+ + | 0xFD || r6...r0 || undefined || 0x00 | // == used block + +------++---------++-----------++------+ + +Relocation link is r6..r0 == atom address. Relocations MUST NOT chain and MUST +point to a "content" block, i.e. one with the first byte in 0x00...0xFC. + +Relocated block allows to permanently assign a handle/file pointer ("atom" +address) to some content and resize the content anytime afterwards w/o having +to update all the possible existing references to the original handle. + +------------------------------------------------------------------------------ + +0xFC: Used long block. + +------++---------++--------------------++---------+---+ + | 0 || 1...2 || 3...N+2 || | | + +------++---------++--------------------++---------+---+ + | 0xFC || n1...n0 || N bytes of content || padding | Z | + +------++---------++--------------------++---------+---+ + +This block type is used for content of length in N == 238...61680 bytes. N is +encoded as a 2 byte unsigned integer n1..n0 in network byte order. Values +bellow 238 are reserved, those content lengths are to be carried by the +0x00..0xFB block types. + + 1. n in 0x00EE...0xF0F0 is used for content under the same rules + as in the 0x01..0xED type. + + 2. If the last byte of the content is not the last byte of an atom then + the last byte of the block is 0x00. + + 3. If the last byte of the content IS the last byte of an atom: + + 3.1 If the last byte of content is in 0x00..0xFD then everything is OK. + + 3.2 If the last byte of content is 0xFE or 0xFF then the escape + via n > 0xF0F0 MUST be used AND the block's last byte is 0x00 or 0x01, + meaning value 0xFE and 0xFF respectively. + + 4. n in 0xF0F1...0xFFFF is like the escaped 0xEE..0xFB block. + N == 13 + 16(n - 0xF0F1). + +Discussion of the padding and Z fields - see the 0x01..0xED block type. + +------------------------------------------------------------------------------ + +0xEE...0xFB: Used escaped short block. + +---++----------------------++---+ + | 0 || 1...N-1 || | + +---++----------------------++---+ + | X || N-1 bytes of content || Z | + +---++----------------------++---+ + +N == 15 + 16(X - 0xEE). Z is the content last byte encoded as follows. + +case Z == 0x00: The last byte of content is 0xFE + +case Z == 0x01: The last byte of content is 0xFF + +------------------------------------------------------------------------------ + +0x01...0xED: Used short block. + +---++--------------------++---------+---+ + | 0 || 1...N || | | + +---++--------------------++---------+---+ + | N || N bytes of content || padding | Z | + +---++--------------------++---------+---+ + +This block type is used for content of length in 1...237 bytes. The value of +the "padding" field, if of non zero length, is undefined. + +If the last byte of content is the last byte of an atom (== its file byte +offset & 0xF == 0xF) then such last byte MUST be in 0x00...0xFD. + +If the last byte of content is the last byte of an atom AND the last byte of +content is 0xFE or 0xFF then the short escape block type (0xEE...0xFB) MUST be +used. + +If the last byte of content is not the last byte of an atom, then the last byte +of such block, i.e. the Z field, which is also a last byte of some atom, MUST +be 0x00 (i.e. the used block marker). Other "tail" values are reserved. + +------------------------------------------------------------------------------ + +0x00: Used empty block. + +------++-----------++------+ + | 0 || 1...14 || 15 | + +------++-----------++------+ + | 0x00 || undefined || 0x00 | // == used block, other "tail" values reserved. + +------++-----------++------+ + +All of the rules for 0x01..0xED applies. Depicted only for its different +semantics (e.g. an allocated [existing] string but with length of zero). + +============================================================================== + +Block last byte + +------------------------------------------------------------------------------ + +0xFF: Free atom. Layout - see "Block first byte": FF. + +------------------------------------------------------------------------------ + +0xFE: Free block, size n atoms. Preceding 7 bytes == size (s6...s0) of the free +block in atoms, network byte order + --++---------++------+ + || -8...-2 || -1 | + --++---------++------+ + ... || s6...s0 || 0xFE | <- block's last byte + --++---------++------+ + +Layout at start of this block - see "Block first byte": FE. + +------------------------------------------------------------------------------ + +0x00...0xFD: Used (non free) block. + +============================================================================== + +Free lists table + +The free lists table content is stored in the standard layout of a used block. + +A table item is a 7 byte size field followed by a 7 byte atom address field +(both in network byte order), thus every item is 14 contiguous bytes. The +item's address field is pointing to a free block. The size field determines +the minimal size (in atoms) of free blocks on that list. + +The free list table is n above items, thus the content has 14n bytes. Note that +the largest block content is 61680 bytes and as there are 14 bytes per table +item, so the table is limited to at most 4405 entries. + +Items in the table do not have to be sorted according to their size field values. + +No two items can have the same value of the size field. + +When freeing blocks, the block MUST be linked into an item list with the +highest possible size field, which is less or equal to the number of atoms in +the new free block. + +When freeing a block, the block MUST be first merged with any adjacent free +blocks (thus possibly creating a bigger free block) using information derived +from the adjacent blocks first and last bytes. Such merged free blocks MUST be +removed from their original doubly linked lists. Afterwards the new bigger free +block is put to the free list table in the appropriate item. + +Items with address field == 0 are legal. Such item is a placeholder for a empty +list of free blocks of the item's size. + +Items with size field == 0 are legal. Such item is a placeholder, used e.g. to +avoid further reallocations/redirecting of the free lists table. + +The largest possible allocation request (for content length 61680 bytes) is +0xF10 (3856) atoms. All free blocks of this or bigger size are presumably put +into a single table item with the size 3856. It may be useful to additionally +have a free lists table item which links free blocks of some bigger size (say +1M+) and then use the OS sparse file support (if present) to save the physical +space used by such free blocks. + +Smaller (<3856 atoms) free blocks can be organized exactly (every distinct size +has its table item) or the sizes can run using other schema like e.g. "1, 2, +4, 8, ..." (powers of 2) or "1, 2, 3, 5, 8, 13, ..." (the Fibonacci sequence) +or they may be fine tuned to a specific usage pattern. + +============================================================================== + +Header + +The first block of a file (atom address == file offset == 0) is the file header. +The header block has the standard layout of a used short non escaped block. + +Special conditions apply: The header block and its content MUST be like this: + + +------+---------+---------+------+ + | 0 | 1...7 | 8...14 | 15 | + +------+---------+---------+------+ + | 0x0F | m6...m0 | f6...f0 | FLTT | + +------+---------+---------+------+ + +m6..m0 is a "magic" value 0xF1C1A1FE51B1E. + +f6...f0 is the atom address of the free lists table (discussed elsewhere). +If f6...f0 == 0x00 the there is no free lists table (yet). + +FLTT describes the type of the Free List Table. Currently defined values: + +------------------------------------------------------------------------------ + +FLTT == 0: Free List Table is fixed at atom address 2. It has a fixed size for 3856 entries +for free list of size 1..3855 atoms and the last is for the list of free block >= 3856 atoms. +*/ +package falloc + +const ( + INVALID_HANDLE = Handle(-1) +) diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/error.go b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/error.go new file mode 100644 index 000000000..dad3d29e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/error.go @@ -0,0 +1,130 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package falloc + +import "fmt" + +// EBadRequest is an error produced for invalid operation, e.g. for data of more than maximum allowed. +type EBadRequest struct { + Name string + Size int +} + +func (e *EBadRequest) Error() string { + return fmt.Sprintf("%s: size %d", e.Name, e.Size) +} + +// EClose is a file/store close error. +type EClose struct { + Name string + Err error +} + +func (e *EClose) Error() string { + return fmt.Sprintf("%sx: %s", e.Name, e.Err) +} + +// ECorrupted is a file/store format error. +type ECorrupted struct { + Name string + Ofs int64 +} + +func (e *ECorrupted) Error() string { + return fmt.Sprintf("%s: corrupted data @%#x", e.Name, e.Ofs) +} + +// ECreate is a file/store create error. +type ECreate struct { + Name string + Err error +} + +func (e *ECreate) Error() string { + return fmt.Sprintf("%s: %s", e.Name, e.Err) +} + +// EFreeList is a file/store format error. +type EFreeList struct { + Name string + Size int64 + Block int64 +} + +func (e *EFreeList) Error() string { + return fmt.Sprintf("%s: invalid free list item, size %#x, block %#x", e.Name, e.Size, e.Block) +} + +// EHandle is an error type reported for invalid Handles. +type EHandle struct { + Name string + Handle Handle +} + +func (e EHandle) Error() string { + return fmt.Sprintf("%s: invalid handle %#x", e.Name, e.Handle) +} + +// EHeader is a file/store format error. +type EHeader struct { + Name string + Header []byte + Expected []byte +} + +func (e *EHeader) Error() string { + return fmt.Sprintf("%s: invalid header, got [% x], expected [% x]", e.Name, e.Header, e.Expected) +} + +// ENullHandle is a file/store access error via a null handle. +type ENullHandle string + +func (e ENullHandle) Error() string { + return fmt.Sprintf("%s: access via null handle", e) +} + +// EOpen is a file/store open error. +type EOpen struct { + Name string + Err error +} + +func (e *EOpen) Error() string { + return fmt.Sprintf("%s: %s", e.Name, e.Err) +} + +// ERead is a file/store read error. +type ERead struct { + Name string + Ofs int64 + Err error +} + +func (e *ERead) Error() string { + return fmt.Sprintf("%s, %#x: %s", e.Name, e.Ofs, e.Err) +} + +// ESize is a file/store size error. +type ESize struct { + Name string + Size int64 +} + +func (e *ESize) Error() string { + return fmt.Sprintf("%s: invalid size %#x(%d), size %%16 != 0", e.Name, e.Size, e.Size) +} + +// EWrite is a file/store write error. +type EWrite struct { + Name string + Ofs int64 + Err error +} + +func (e *EWrite) Error() string { + return fmt.Sprintf("%s, %#x: %s", e.Name, e.Ofs, e.Err) +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/falloc.go b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/falloc.go new file mode 100644 index 000000000..066a7047f --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/falloc.go @@ -0,0 +1,676 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +/* + +This is an mostly (WIP) conforming implementation of the "specs" in docs.go. + +The main incompletness is support for only one kind of FTL, though this table kind is still per "specs". + +*/ + +package falloc + +import ( + "bytes" + "github.com/cznic/fileutil/storage" + "sync" +) + +// Handle is a reference to a block in a file/store. +// Handle is an uint56 wrapped in an in64, i.e. the most significant byte must be always zero. +type Handle int64 + +// Put puts the 7 least significant bytes of h into b. The MSB of h should be zero. +func (h Handle) Put(b []byte) { + for ofs := 6; ofs >= 0; ofs-- { + b[ofs] = byte(h) + h >>= 8 + } +} + +// Get gets the 7 least significant bytes of h from b. The MSB of h is zeroed. +func (h *Handle) Get(b []byte) { + var x Handle + for ofs := 0; ofs <= 6; ofs++ { + x = x<<8 | Handle(b[ofs]) + } + *h = x +} + +// File is a file/store with space allocation/deallocation support. +type File struct { + f storage.Accessor + atoms int64 // current file size in atom units + canfree int64 // only blocks >= canfree can be subject to Free() + freetab [3857]int64 // freetab[0] is unused, freetab[1] is size 1 ptr, freetab[2] is size 2 ptr, ... + rwm sync.RWMutex +} + +func (f *File) read(b []byte, off int64) { + if n, err := f.f.ReadAt(b, off); n != len(b) { + panic(&ERead{f.f.Name(), off, err}) + } +} + +func (f *File) write(b []byte, off int64) { + if n, err := f.f.WriteAt(b, off); n != len(b) { + panic(&EWrite{f.f.Name(), off, err}) + } +} + +var ( // R/O + hdr = []byte{0x0f, 0xf1, 0xc1, 0xa1, 0xfe, 0xa5, 0x1b, 0x1e, 0, 0, 0, 0, 0, 0, 2, 0} // free lists table @2 + empty = make([]byte, 16) + zero = []byte{0} + zero7 = make([]byte, 7) +) + +// New returns a new File backed by store or an error if any. +// Any existing data in store are discarded. +func New(store storage.Accessor) (f *File, err error) { + f = &File{f: store} + return f, storage.Mutate(store, func() (err error) { + if err = f.f.Truncate(0); err != nil { + return &ECreate{f.f.Name(), err} + } + + if _, err = f.Alloc(hdr[1:]); err != nil { //TODO internal panicking versions of the exported fns. + return + } + + if _, err = f.Alloc(nil); err != nil { // (empty) root @1 + return + } + + b := make([]byte, 3856*14) + for i := 1; i <= 3856; i++ { + Handle(i).Put(b[(i-1)*14:]) + } + if _, err = f.Alloc(b); err != nil { + return + } + + f.canfree = f.atoms + return + }) +} + +// Open returns a new File backed by store or an error if any. +// Store already has to be in a valid format. +func Open(store storage.Accessor) (f *File, err error) { + defer func() { + if e := recover(); e != nil { + f = nil + err = e.(error) + } + }() + + fi, err := store.Stat() + if err != nil { + panic(&EOpen{store.Name(), err}) + } + + fs := fi.Size() + if fs&0xf != 0 { + panic(&ESize{store.Name(), fi.Size()}) + } + + f = &File{f: store, atoms: fs >> 4} + b := make([]byte, len(hdr)) + f.read(b, 0) + if !bytes.Equal(b, hdr) { + panic(&EHeader{store.Name(), b, append([]byte{}, hdr...)}) + } + + var atoms int64 + b, atoms = f.readUsed(2) + f.canfree = atoms + 2 + ofs := 0 + var size, p Handle + for ofs < len(b) { + size.Get(b[ofs:]) + ofs += 7 + p.Get(b[ofs:]) + ofs += 7 + if sz, pp := int64(size), int64(p); size == 0 || size > 3856 || (pp != 0 && pp < f.canfree) || pp<<4 > fs-16 { + panic(&EFreeList{store.Name(), sz, pp}) + } + + f.freetab[size] = int64(p) + } + return +} + +// Accessor returns the File's underlying Accessor. +func (f *File) Accessor() storage.Accessor { + return f.f +} + +// Close closes f and returns an error if any. +func (f *File) Close() (err error) { + return storage.Mutate(f.Accessor(), func() (err error) { + if err = f.f.Close(); err != nil { + err = &EClose{f.f.Name(), err} + } + return + }) +} + +// Root returns the handle of the DB root (top level directory, ...). +func (f *File) Root() Handle { + return 1 +} + +func (f *File) readUsed(atom int64) (content []byte, atoms int64) { + b, redirected := make([]byte, 7), false +redir: + ofs := atom << 4 + f.read(b[:1], ofs) + switch pre := b[0]; { + default: + panic(&ECorrupted{f.f.Name(), ofs}) + case pre == 0x00: // Empty block + case pre >= 1 && pre <= 237: // Short + content = make([]byte, pre) + f.read(content, ofs+1) + case pre >= 0xee && pre <= 0xfb: // Short esc + content = make([]byte, 15+16*(pre-0xee)) + f.read(content, ofs+1) + content[len(content)-1] += 0xfe + case pre == 0xfc: // Long + f.read(b[:2], ofs+1) + n := int(b[0])<<8 + int(b[1]) + switch { + default: + panic(&ECorrupted{f.f.Name(), ofs + 1}) + case n >= 238 && n <= 61680: // Long non esc + content = make([]byte, n) + f.read(content, ofs+3) + case n >= 61681: // Long esc + content = make([]byte, 13+16*(n-0xf0f1)) + f.read(content, ofs+3) + content[len(content)-1] += 0xfe + } + case pre == 0xfd: // redir + if redirected { + panic(&ECorrupted{f.f.Name(), ofs}) + } + + f.read(b[:7], ofs+1) + (*Handle)(&atom).Get(b) + redirected = true + goto redir + } + return content, rq2Atoms(len(content)) +} + +func (f *File) writeUsed(b []byte, atom int64) { + n := len(b) + switch ofs, atoms, endmark := atom<<4, rq2Atoms(n), true; { + default: + panic("internal error") + case n == 0: + f.write(empty, ofs) + case n <= 237: + if (n+1)&0xf == 0 { // content end == atom end + if v := b[n-1]; v >= 0xfe { // escape + pre := []byte{byte((16*0xee + n - 15) >> 4)} + f.write(pre, ofs) + f.write(b[:n-1], ofs+1) + f.write([]byte{v - 0xfe}, ofs+atoms<<4-1) + return + } + endmark = false + } + // non esacpe + pre := []byte{byte(n)} + f.write(pre, ofs) + f.write(b, ofs+1) + if endmark { + f.write(zero, ofs+atoms<<4-1) // last block byte <- used block + } + case n > 237 && n <= 61680: + if (n+3)&0xf == 0 { // content end == atom end + if v := b[n-1]; v >= 0xfe { // escape + x := (16*0xf0f1 + n - 13) >> 4 + pre := []byte{0xFC, byte(x >> 8), byte(x)} + f.write(pre, ofs) + f.write(b[:n-1], ofs+3) + f.write([]byte{v - 0xfe}, ofs+atoms<<4-1) + return + } + endmark = false + } + // non esacpe + pre := []byte{0xfc, byte(n >> 8), byte(n)} + f.write(pre, ofs) + f.write(b, ofs+3) + if endmark { + f.write(zero, ofs+atoms<<4-1) // last block byte <- used block + } + } +} + +func rq2Atoms(rqbytes int) (rqatoms int64) { + if rqbytes > 237 { + rqbytes += 2 + } + return int64(rqbytes>>4 + 1) +} + +func (f *File) extend(b []byte) (handle int64) { + handle = f.atoms + f.writeUsed(b, handle) + f.atoms += rq2Atoms(len(b)) + return +} + +// Alloc stores b in a newly allocated space and returns its handle and an error if any. +func (f *File) Alloc(b []byte) (handle Handle, err error) { + err = storage.Mutate(f.Accessor(), func() (err error) { + rqAtoms := rq2Atoms(len(b)) + if rqAtoms > 3856 { + return &EBadRequest{f.f.Name(), len(b)} + } + + for foundsize, foundp := range f.freetab[rqAtoms:] { + if foundp != 0 { + // this works only for the current unique sizes list (except the last item!) + size := int64(foundsize) + rqAtoms + handle = Handle(foundp) + if size == 3856 { + buf := make([]byte, 7) + f.read(buf, int64(handle)<<4+15) + (*Handle)(&size).Get(buf) + } + f.delFree(int64(handle), size) + if rqAtoms < size { + f.addFree(int64(handle)+rqAtoms, size-rqAtoms) + } + f.writeUsed(b, int64(handle)) + return + } + } + + handle = Handle(f.extend(b)) + return + }) + return +} + +// checkLeft returns the atom size of a free bleck left adjacent to block @atom. +// If that block is not free the returned size is 0. +func (f *File) checkLeft(atom int64) (size int64) { + if atom <= f.canfree { + return + } + + b := make([]byte, 7) + fp := atom << 4 + f.read(b[:1], fp-1) + switch last := b[0]; { + case last <= 0xfd: + // used block + case last == 0xfe: + f.read(b, fp-8) + (*Handle)(&size).Get(b) + case last == 0xff: + size = 1 + } + return +} + +// getInfo returns the block @atom type and size. +func (f *File) getInfo(atom int64) (pref byte, size int64) { + b := make([]byte, 7) + fp := atom << 4 + f.read(b[:1], fp) + switch pref = b[0]; { + case pref == 0: // Empty used + size = 1 + case pref >= 1 && pref <= 237: // Short + size = rq2Atoms(int(pref)) + case pref >= 0xee && pref <= 0xfb: // Short esc + size = rq2Atoms(15 + 16*int(pref-0xee)) + case pref == 0xfc: // Long + f.read(b[:2], fp+1) + n := int(b[0])<<8 + int(b[1]) + switch { + default: + panic(&ECorrupted{f.f.Name(), fp + 1}) + case n >= 238 && n <= 61680: // Long non esc + size = rq2Atoms(n) + case n >= 61681: // Long esc + size = rq2Atoms(13 + 16*(n-0xf0f1)) + } + case pref == 0xfd: // reloc + size = 1 + case pref == 0xfe: + f.read(b, fp+15) + (*Handle)(&size).Get(b) + case pref == 0xff: + size = 1 + } + return +} + +// getSize returns the atom size of the block @atom and wheter it is free. +func (f *File) getSize(atom int64) (size int64, isFree bool) { + var typ byte + typ, size = f.getInfo(atom) + isFree = typ >= 0xfe + return +} + +// checkRight returns the atom size of a free bleck right adjacent to block @atom,atoms. +// If that block is not free the returned size is 0. +func (f *File) checkRight(atom, atoms int64) (size int64) { + if atom+atoms >= f.atoms { + return + } + + if sz, free := f.getSize(atom + atoms); free { + size = sz + } + return +} + +// delFree removes the atoms@atom free block from the free block list +func (f *File) delFree(atom, atoms int64) { + b := make([]byte, 15) + size := int(atoms) + if n := len(f.freetab); atoms >= int64(n) { + size = n - 1 + } + fp := atom << 4 + f.read(b[1:], fp+1) + var prev, next Handle + prev.Get(b[1:]) + next.Get(b[8:]) + + switch { + case prev == 0 && next != 0: + next.Put(b) + f.write(b[:7], int64(32+3+7+(size-1)*14)) + f.write(zero7, int64(next)<<4+1) + f.freetab[size] = int64(next) + case prev != 0 && next == 0: + f.write(zero7, int64(prev)<<4+8) + case prev != 0 && next != 0: + prev.Put(b) + f.write(b[:7], int64(next)<<4+1) + next.Put(b) + f.write(b[:7], int64(prev)<<4+8) + default: // prev == 0 && next == 0: + f.write(zero7, int64(32+3+7+(size-1)*14)) + f.freetab[size] = 0 + } +} + +// addFree adds atoms@atom to the free block lists and marks it free. +func (f *File) addFree(atom, atoms int64) { + b := make([]byte, 7) + size := int(atoms) + if n := len(f.freetab); atoms >= int64(n) { + size = n - 1 + } + head := f.freetab[size] + if head == 0 { // empty list + f.makeFree(0, atom, atoms, 0) + Handle(atom).Put(b) + f.write(b, int64(32+3+7+(size-1)*14)) + f.freetab[size] = atom + return + } + + Handle(atom).Put(b) + f.write(b, head<<4+1) // head.prev = atom + f.makeFree(0, atom, atoms, head) // atom.next = head + f.write(b, int64(32+3+7+(size-1)*14)) + f.freetab[size] = atom +} + +// makeFree sets up the content of a free block atoms@atom, fills the prev and next links. +func (f *File) makeFree(prev, atom, atoms, next int64) { + b := make([]byte, 23) + fp := atom << 4 + if atoms == 1 { + b[0] = 0xff + Handle(prev).Put(b[1:]) + Handle(next).Put(b[8:]) + b[15] = 0xff + f.write(b[:16], fp) + return + } + + b[0] = 0xfe + Handle(prev).Put(b[1:]) + Handle(next).Put(b[8:]) + Handle(atoms).Put(b[15:]) + f.write(b[:22], fp) + b[22] = 0xfe + f.write(b[15:], fp+atoms<<4-8) +} + +// Read reads and return the data associated with handle and an error if any. +// Passing an invalid handle to Read may return invalid data without error. +// It's like getting garbage via passing an invalid pointer to C.memcopy(). +func (f *File) Read(handle Handle) (b []byte, err error) { + defer func() { + if e := recover(); e != nil { + b = nil + err = e.(error) + } + }() + + switch handle { + case 0: + panic(ENullHandle(f.f.Name())) + case 2: + panic(&EHandle{f.f.Name(), handle}) + default: + b, _ = f.readUsed(int64(handle)) + } + return +} + +// Free frees space associated with handle and returns an error if any. Passing an invalid +// handle to Free or reusing handle afterwards will probably corrupt the database or provide +// invalid data on Read. It's like corrupting memory via passing an invalid pointer to C.free() +// or reusing that pointer. +func (f *File) Free(handle Handle) (err error) { + return storage.Mutate(f.Accessor(), func() (err error) { + atom := int64(handle) + atoms, isFree := f.getSize(atom) + if isFree || atom < f.canfree { + return &EHandle{f.f.Name(), handle} + } + + leftFree, rightFree := f.checkLeft(atom), f.checkRight(atom, atoms) + switch { + case leftFree != 0 && rightFree != 0: + f.delFree(atom-leftFree, leftFree) + f.delFree(atom+atoms, rightFree) + f.addFree(atom-leftFree, leftFree+atoms+rightFree) + case leftFree != 0 && rightFree == 0: + f.delFree(atom-leftFree, leftFree) + if atom+atoms == f.atoms { // the left free neighbour and this block together are an empy tail + f.atoms = atom - leftFree + f.f.Truncate(f.atoms << 4) + return + } + + f.addFree(atom-leftFree, leftFree+atoms) + case leftFree == 0 && rightFree != 0: + f.delFree(atom+atoms, rightFree) + f.addFree(atom, atoms+rightFree) + default: // leftFree == 0 && rightFree == 0 + if atom+atoms < f.atoms { // isolated inner block + f.addFree(atom, atoms) + return + } + + f.f.Truncate(atom << 4) // isolated tail block, shrink file + f.atoms = atom + } + return + }) +} + +// Realloc reallocates space associted with handle to acomodate b, returns the newhandle +// newly associated with b and an error if any. If keepHandle == true then Realloc guarantees +// newhandle == handle even if the new data are larger then the previous content associated +// with handle. If !keepHandle && newhandle != handle then reusing handle will probably corrupt +// the database. +// The above effects are like corrupting memory/data via passing an invalid pointer to C.realloc(). +func (f *File) Realloc(handle Handle, b []byte, keepHandle bool) (newhandle Handle, err error) { + err = storage.Mutate(f.Accessor(), func() (err error) { + switch handle { + case 0, 2: + return &EHandle{f.f.Name(), handle} + case 1: + keepHandle = true + } + newhandle = handle + atom, newatoms := int64(handle), rq2Atoms(len(b)) + if newatoms > 3856 { + return &EBadRequest{f.f.Name(), len(b)} + } + + typ, oldatoms := f.getInfo(atom) + switch { + default: + return &ECorrupted{f.f.Name(), atom << 4} + case typ <= 0xfc: // non relocated used block + switch { + case newatoms == oldatoms: // in place replace + f.writeUsed(b, atom) + case newatoms < oldatoms: // in place shrink + rightFree := f.checkRight(atom, oldatoms) + if rightFree > 0 { // right join + f.delFree(atom+oldatoms, rightFree) + } + f.addFree(atom+newatoms, oldatoms+rightFree-newatoms) + f.writeUsed(b, atom) + case newatoms > oldatoms: + if rightFree := f.checkRight(atom, oldatoms); rightFree > 0 && newatoms <= oldatoms+rightFree { + f.delFree(atom+oldatoms, rightFree) + if newatoms < oldatoms+rightFree { + f.addFree(atom+newatoms, oldatoms+rightFree-newatoms) + } + f.writeUsed(b, atom) + return + } + + if !keepHandle { + f.Free(Handle(atom)) + newhandle, err = f.Alloc(b) + return + } + + // reloc + newatom, e := f.Alloc(b) + if e != nil { + return e + } + + buf := make([]byte, 16) + buf[0] = 0xfd + Handle(newatom).Put(buf[1:]) + f.Realloc(Handle(atom), buf[1:], true) + f.write(buf[:1], atom<<4) + } + case typ == 0xfd: // reloc + var target Handle + buf := make([]byte, 7) + f.read(buf, atom<<4+1) + target.Get(buf) + switch { + case newatoms == 1: + f.writeUsed(b, atom) + f.Free(target) + default: + if rightFree := f.checkRight(atom, 1); rightFree > 0 && newatoms <= 1+rightFree { + f.delFree(atom+1, rightFree) + if newatoms < 1+rightFree { + f.addFree(atom+newatoms, 1+rightFree-newatoms) + } + f.writeUsed(b, atom) + f.Free(target) + return + } + + newtarget, e := f.Realloc(Handle(target), b, false) + if e != nil { + return e + } + + if newtarget != target { + Handle(newtarget).Put(buf) + f.write(buf, atom<<4+1) + } + } + } + return + }) + return +} + +// Lock locks f for writing. If the lock is already locked for reading or writing, +// Lock blocks until the lock is available. To ensure that the lock eventually becomes available, +// a blocked Lock call excludes new readers from acquiring the lock. +func (f *File) Lock() { + f.rwm.Lock() +} + +// RLock locks f for reading. If the lock is already locked for writing or there is a writer +// already waiting to release the lock, RLock blocks until the writer has released the lock. +func (f *File) RLock() { + f.rwm.RLock() +} + +// Unlock unlocks f for writing. It is a run-time error if f is not locked for writing on entry to Unlock. +// +// As with Mutexes, a locked RWMutex is not associated with a particular goroutine. +// One goroutine may RLock (Lock) f and then arrange for another goroutine to RUnlock (Unlock) it. +func (f *File) Unlock() { + f.rwm.Unlock() +} + +// RUnlock undoes a single RLock call; it does not affect other simultaneous readers. +// It is a run-time error if f is not locked for reading on entry to RUnlock. +func (f *File) RUnlock() { + f.rwm.RUnlock() +} + +// LockedAlloc wraps Alloc in a Lock/Unlock pair. +func (f *File) LockedAlloc(b []byte) (handle Handle, err error) { + f.Lock() + defer f.Unlock() + return f.Alloc(b) +} + +// LockedFree wraps Free in a Lock/Unlock pair. +func (f *File) LockedFree(handle Handle) (err error) { + f.Lock() + defer f.Unlock() + return f.Free(handle) +} + +// LockedRead wraps Read in a RLock/RUnlock pair. +func (f *File) LockedRead(handle Handle) (b []byte, err error) { + f.RLock() + defer f.RUnlock() + return f.Read(handle) +} + +// LockedRealloc wraps Realloc in a Lock/Unlock pair. +func (f *File) LockedRealloc(handle Handle, b []byte, keepHandle bool) (newhandle Handle, err error) { + f.Lock() + defer f.Unlock() + return f.Realloc(handle, b, keepHandle) +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/test_deps.go b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/test_deps.go new file mode 100644 index 000000000..3bdd39f2b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/falloc/test_deps.go @@ -0,0 +1,15 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package falloc + +// Pull test dependencies too. +// Enables easy 'go test X' after 'go get X' +import ( + _ "github.com/cznic/fileutil" + _ "github.com/cznic/fileutil/storage" + _ "github.com/cznic/mathutil" +) diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil.go new file mode 100644 index 000000000..2f0f7ab15 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil.go @@ -0,0 +1,223 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fileutil collects some file utility functions. +package fileutil + +import ( + "fmt" + "io" + "os" + "path/filepath" + "runtime" + "strconv" + "sync" + "time" +) + +// GoMFile is a concurrent access safe version of MFile. +type GoMFile struct { + mfile *MFile + mutex sync.Mutex +} + +// NewGoMFile return a newly created GoMFile. +func NewGoMFile(fname string, flag int, perm os.FileMode, delta_ns int64) (m *GoMFile, err error) { + m = &GoMFile{} + if m.mfile, err = NewMFile(fname, flag, perm, delta_ns); err != nil { + m = nil + } + return +} + +func (m *GoMFile) File() (file *os.File, err error) { + m.mutex.Lock() + defer m.mutex.Unlock() + return m.mfile.File() +} + +func (m *GoMFile) SetChanged() { + m.mutex.Lock() + defer m.mutex.Unlock() + m.mfile.SetChanged() +} + +func (m *GoMFile) SetHandler(h MFileHandler) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.mfile.SetHandler(h) +} + +// MFileHandler resolves modifications of File. +// Possible File context is expected to be a part of the handler's closure. +type MFileHandler func(*os.File) error + +// MFile represents an os.File with a guard/handler on change/modification. +// Example use case is an app with a configuration file which can be modified at any time +// and have to be reloaded in such event prior to performing something configurable by that +// file. The checks are made only on access to the MFile file by +// File() and a time threshold/hysteresis value can be chosen on creating a new MFile. +type MFile struct { + file *os.File + handler MFileHandler + t0 int64 + delta int64 + ctime int64 +} + +// NewMFile returns a newly created MFile or Error if any. +// The fname, flag and perm parameters have the same meaning as in os.Open. +// For meaning of the delta_ns parameter please see the (m *MFile) File() docs. +func NewMFile(fname string, flag int, perm os.FileMode, delta_ns int64) (m *MFile, err error) { + m = &MFile{} + m.t0 = time.Now().UnixNano() + if m.file, err = os.OpenFile(fname, flag, perm); err != nil { + return + } + + var fi os.FileInfo + if fi, err = m.file.Stat(); err != nil { + return + } + + m.ctime = fi.ModTime().UnixNano() + m.delta = delta_ns + runtime.SetFinalizer(m, func(m *MFile) { + m.file.Close() + }) + return +} + +// SetChanged forces next File() to unconditionally handle modification of the wrapped os.File. +func (m *MFile) SetChanged() { + m.ctime = -1 +} + +// SetHandler sets a function to be invoked when modification of MFile is to be processed. +func (m *MFile) SetHandler(h MFileHandler) { + m.handler = h +} + +// File returns an os.File from MFile. If time elapsed between the last invocation of this function +// and now is at least delta_ns ns (a parameter of NewMFile) then the file is checked for +// change/modification. For delta_ns == 0 the modification is checked w/o getting os.Time(). +// If a change is detected a handler is invoked on the MFile file. +// Any of these steps can produce an Error. If that happens the function returns nil, Error. +func (m *MFile) File() (file *os.File, err error) { + var now int64 + + mustCheck := m.delta == 0 + if !mustCheck { + now = time.Now().UnixNano() + mustCheck = now-m.t0 > m.delta + } + + if mustCheck { // check interval reached + var fi os.FileInfo + if fi, err = m.file.Stat(); err != nil { + return + } + + if fi.ModTime().UnixNano() != m.ctime { // modification detected + if m.handler == nil { + return nil, fmt.Errorf("no handler set for modified file %q", m.file.Name()) + } + if err = m.handler(m.file); err != nil { + return + } + + m.ctime = fi.ModTime().UnixNano() + } + m.t0 = now + } + + return m.file, nil +} + +// Read reads buf from r. It will either fill the full buf or fail. +// It wraps the functionality of an io.Reader which may return less bytes than requested, +// but may block if not all data are ready for the io.Reader. +func Read(r io.Reader, buf []byte) (err error) { + have := 0 + remain := len(buf) + got := 0 + for remain > 0 { + if got, err = r.Read(buf[have:]); err != nil { + return + } + + remain -= got + have += got + } + return +} + +// "os" and/or "syscall" extensions + +// FadviseAdvice is used by Fadvise. +type FadviseAdvice int + +// FAdviseAdvice values. +const ( + // $ grep FADV /usr/include/bits/fcntl.h + POSIX_FADV_NORMAL FadviseAdvice = iota // No further special treatment. + POSIX_FADV_RANDOM // Expect random page references. + POSIX_FADV_SEQUENTIAL // Expect sequential page references. + POSIX_FADV_WILLNEED // Will need these pages. + POSIX_FADV_DONTNEED // Don't need these pages. + POSIX_FADV_NOREUSE // Data will be accessed once. +) + +// TempFile creates a new temporary file in the directory dir with a name +// ending with suffix, basename starting with prefix, opens the file for +// reading and writing, and returns the resulting *os.File. If dir is the +// empty string, TempFile uses the default directory for temporary files (see +// os.TempDir). Multiple programs calling TempFile simultaneously will not +// choose the same file. The caller can use f.Name() to find the pathname of +// the file. It is the caller's responsibility to remove the file when no +// longer needed. +// +// NOTE: This function differs from ioutil.TempFile. +func TempFile(dir, prefix, suffix string) (f *os.File, err error) { + if dir == "" { + dir = os.TempDir() + } + + nconflict := 0 + for i := 0; i < 10000; i++ { + name := filepath.Join(dir, prefix+nextInfix()+suffix) + f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + if os.IsExist(err) { + if nconflict++; nconflict > 10 { + rand = reseed() + } + continue + } + break + } + return +} + +// Random number state. +// We generate random temporary file names so that there's a good +// chance the file doesn't exist yet - keeps the number of tries in +// TempFile to a minimum. +var rand uint32 +var randmu sync.Mutex + +func reseed() uint32 { + return uint32(time.Now().UnixNano() + int64(os.Getpid())) +} + +func nextInfix() string { + randmu.Lock() + r := rand + if r == 0 { + r = reseed() + } + r = r*1664525 + 1013904223 // constants from Numerical Recipes + rand = r + randmu.Unlock() + return strconv.Itoa(int(1e9 + r%1e9))[1:] +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_arm.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_arm.go new file mode 100644 index 000000000..9410d1bb2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_arm.go @@ -0,0 +1,25 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fileutil + +import ( + "io" + "os" +) + +// PunchHole deallocates space inside a file in the byte range starting at +// offset and continuing for len bytes. Not supported on ARM. +func PunchHole(f *os.File, off, len int64) error { + return nil +} + +// Fadvise predeclares an access pattern for file data. See also 'man 2 +// posix_fadvise'. Not supported on ARM. +func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error { + return nil +} + +// IsEOF reports whether err is an EOF condition. +func IsEOF(err error) bool { return err == io.EOF } diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_darwin.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_darwin.go new file mode 100644 index 000000000..a19723fc7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_darwin.go @@ -0,0 +1,25 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fileutil + +import ( + "io" + "os" +) + +// PunchHole deallocates space inside a file in the byte range starting at +// offset and continuing for len bytes. Not supported on OSX. +func PunchHole(f *os.File, off, len int64) error { + return nil +} + +// Fadvise predeclares an access pattern for file data. See also 'man 2 +// posix_fadvise'. Not supported on OSX. +func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error { + return nil +} + +// IsEOF reports whether err is an EOF condition. +func IsEOF(err error) bool { return err == io.EOF } diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_freebsd.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_freebsd.go new file mode 100644 index 000000000..cefec0cb3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_freebsd.go @@ -0,0 +1,27 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !arm + +package fileutil + +import ( + "io" + "os" +) + +// PunchHole deallocates space inside a file in the byte range starting at +// offset and continuing for len bytes. Unimplemented on FreeBSD. +func PunchHole(f *os.File, off, len int64) error { + return nil +} + +// Fadvise predeclares an access pattern for file data. See also 'man 2 +// posix_fadvise'. Unimplemented on FreeBSD. +func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error { + return nil +} + +// IsEOF reports whether err is an EOF condition. +func IsEOF(err error) bool { return err == io.EOF } diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_linux.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_linux.go new file mode 100644 index 000000000..8babfc553 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_linux.go @@ -0,0 +1,96 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !arm + +package fileutil + +import ( + "bytes" + "io" + "io/ioutil" + "os" + "strconv" + "syscall" +) + +func n(s []byte) byte { + for i, c := range s { + if c < '0' || c > '9' { + s = s[:i] + break + } + } + v, _ := strconv.Atoi(string(s)) + return byte(v) +} + +func init() { + b, err := ioutil.ReadFile("/proc/sys/kernel/osrelease") + if err != nil { + panic(err) + } + + tokens := bytes.Split(b, []byte(".")) + if len(tokens) > 3 { + tokens = tokens[:3] + } + switch len(tokens) { + case 3: + // Supported since kernel 2.6.38 + if bytes.Compare([]byte{n(tokens[0]), n(tokens[1]), n(tokens[2])}, []byte{2, 6, 38}) < 0 { + puncher = func(*os.File, int64, int64) error { return nil } + } + case 2: + if bytes.Compare([]byte{n(tokens[0]), n(tokens[1])}, []byte{2, 7}) < 0 { + puncher = func(*os.File, int64, int64) error { return nil } + } + default: + puncher = func(*os.File, int64, int64) error { return nil } + } +} + +var puncher = func(f *os.File, off, len int64) error { + const ( + /* + /usr/include/linux$ grep FL_ falloc.h + */ + _FALLOC_FL_KEEP_SIZE = 0x01 // default is extend size + _FALLOC_FL_PUNCH_HOLE = 0x02 // de-allocates range + ) + + _, _, errno := syscall.Syscall6( + syscall.SYS_FALLOCATE, + uintptr(f.Fd()), + uintptr(_FALLOC_FL_KEEP_SIZE|_FALLOC_FL_PUNCH_HOLE), + uintptr(off), + uintptr(len), + 0, 0) + if errno != 0 { + return os.NewSyscallError("SYS_FALLOCATE", errno) + } + return nil +} + +// PunchHole deallocates space inside a file in the byte range starting at +// offset and continuing for len bytes. No-op for kernels < 2.6.38 (or < 2.7). +func PunchHole(f *os.File, off, len int64) error { + return puncher(f, off, len) +} + +// Fadvise predeclares an access pattern for file data. See also 'man 2 +// posix_fadvise'. +func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error { + _, _, errno := syscall.Syscall6( + syscall.SYS_FADVISE64, + uintptr(f.Fd()), + uintptr(off), + uintptr(len), + uintptr(advice), + 0, 0) + return os.NewSyscallError("SYS_FADVISE64", errno) +} + +// IsEOF reports whether err is an EOF condition. +func IsEOF(err error) bool { return err == io.EOF } diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_netbsd.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_netbsd.go new file mode 100644 index 000000000..ca778d6d7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_netbsd.go @@ -0,0 +1,27 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !arm + +package fileutil + +import ( + "io" + "os" +) + +// PunchHole deallocates space inside a file in the byte range starting at +// offset and continuing for len bytes. Similar to FreeBSD, this is +// unimplemented. +func PunchHole(f *os.File, off, len int64) error { + return nil +} + +// Unimplemented on NetBSD. +func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error { + return nil +} + +// IsEOF reports whether err is an EOF condition. +func IsEOF(err error) bool { return err == io.EOF } diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_openbsd.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_openbsd.go new file mode 100644 index 000000000..428171bdf --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_openbsd.go @@ -0,0 +1,25 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fileutil + +import ( + "io" + "os" +) + +// PunchHole deallocates space inside a file in the byte range starting at +// offset and continuing for len bytes. Similar to FreeBSD, this is +// unimplemented. +func PunchHole(f *os.File, off, len int64) error { + return nil +} + +// Unimplemented on OpenBSD. +func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error { + return nil +} + +// IsEOF reports whether err is an EOF condition. +func IsEOF(err error) bool { return err == io.EOF } diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_plan9.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_plan9.go new file mode 100644 index 000000000..a2db64e2d --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_plan9.go @@ -0,0 +1,25 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fileutil + +import ( + "io" + "os" +) + +// PunchHole deallocates space inside a file in the byte range starting at +// offset and continuing for len bytes. Unimplemented on Plan 9. +func PunchHole(f *os.File, off, len int64) error { + return nil +} + +// Fadvise predeclares an access pattern for file data. See also 'man 2 +// posix_fadvise'. Unimplemented on Plan 9. +func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error { + return nil +} + +// IsEOF reports whether err is an EOF condition. +func IsEOF(err error) bool { return err == io.EOF } diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_solaris.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_solaris.go new file mode 100644 index 000000000..61dfcde38 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_solaris.go @@ -0,0 +1,27 @@ +// Copyright (c) 2013 jnml. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.3 + +package fileutil + +import ( + "io" + "os" +) + +// PunchHole deallocates space inside a file in the byte range starting at +// offset and continuing for len bytes. Not supported on Solaris. +func PunchHole(f *os.File, off, len int64) error { + return nil +} + +// Fadvise predeclares an access pattern for file data. See also 'man 2 +// posix_fadvise'. Not supported on Solaris. +func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error { + return nil +} + +// IsEOF reports whether err is an EOF condition. +func IsEOF(err error) bool { return err == io.EOF } diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_windows.go b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_windows.go new file mode 100644 index 000000000..3a81f2fc8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/fileutil_windows.go @@ -0,0 +1,183 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fileutil + +import ( + "io" + "os" + "sync" + "syscall" + "unsafe" +) + +// PunchHole deallocates space inside a file in the byte range starting at +// offset and continuing for len bytes. Not supported on Windows. +func PunchHole(f *os.File, off, len int64) error { + return puncher(f, off, len) +} + +// Fadvise predeclares an access pattern for file data. See also 'man 2 +// posix_fadvise'. Not supported on Windows. +func Fadvise(f *os.File, off, len int64, advice FadviseAdvice) error { + return nil +} + +// IsEOF reports whether err is an EOF condition. +func IsEOF(err error) bool { + if err == io.EOF { + return true + } + + // http://social.technet.microsoft.com/Forums/windowsserver/en-US/1a16311b-c625-46cf-830b-6a26af488435/how-to-solve-error-38-0x26-errorhandleeof-using-fsctlgetretrievalpointers + x, ok := err.(*os.PathError) + return ok && x.Op == "read" && x.Err.(syscall.Errno) == 0x26 +} + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procDeviceIOControl = modkernel32.NewProc("DeviceIoControl") + + sparseFilesMu sync.Mutex + sparseFiles map[uintptr]struct{} +) + +func init() { + // sparseFiles is an fd set for already "sparsed" files - according to + // msdn.microsoft.com/en-us/library/windows/desktop/aa364225(v=vs.85).aspx + // the file handles are unique per process. + sparseFiles = make(map[uintptr]struct{}) +} + +// puncHoleWindows punches a hole into the given file starting at offset, +// measuring "size" bytes +// (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364597%28v=vs.85%29.aspx) +func puncher(file *os.File, offset, size int64) error { + if err := ensureFileSparse(file); err != nil { + return err + } + + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364411%28v=vs.85%29.aspx + // typedef struct _FILE_ZERO_DATA_INFORMATION { + // LARGE_INTEGER FileOffset; + // LARGE_INTEGER BeyondFinalZero; + //} FILE_ZERO_DATA_INFORMATION, *PFILE_ZERO_DATA_INFORMATION; + type fileZeroDataInformation struct { + FileOffset, BeyondFinalZero int64 + } + + lpInBuffer := fileZeroDataInformation{ + FileOffset: offset, + BeyondFinalZero: offset + size} + return deviceIOControl(false, file.Fd(), uintptr(unsafe.Pointer(&lpInBuffer)), 16) +} + +// // http://msdn.microsoft.com/en-us/library/windows/desktop/cc948908%28v=vs.85%29.aspx +// type fileSetSparseBuffer struct { +// SetSparse bool +// } + +func ensureFileSparse(file *os.File) (err error) { + fd := file.Fd() + sparseFilesMu.Lock() + if _, ok := sparseFiles[fd]; ok { + sparseFilesMu.Unlock() + return nil + } + + if err = deviceIOControl(true, fd, 0, 0); err == nil { + sparseFiles[fd] = struct{}{} + } + sparseFilesMu.Unlock() + return err +} + +func deviceIOControl(setSparse bool, fd, inBuf, inBufLen uintptr) (err error) { + const ( + //http://source.winehq.org/source/include/winnt.h#L4605 + file_read_data = 1 + file_write_data = 2 + + // METHOD_BUFFERED 0 + method_buffered = 0 + // FILE_ANY_ACCESS 0 + file_any_access = 0 + // FILE_DEVICE_FILE_SYSTEM 0x00000009 + file_device_file_system = 0x00000009 + // FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS) + file_special_access = file_any_access + file_read_access = file_read_data + file_write_access = file_write_data + + // http://source.winehq.org/source/include/winioctl.h + // #define CTL_CODE ( DeviceType, + // Function, + // Method, + // Access ) + // ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) + + // FSCTL_SET_COMPRESSION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 16, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) + fsctl_set_compression = (file_device_file_system << 16) | ((file_read_access | file_write_access) << 14) | (16 << 2) | method_buffered + // FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) + fsctl_set_sparse = (file_device_file_system << 16) | (file_special_access << 14) | (49 << 2) | method_buffered + // FSCTL_SET_ZERO_DATA CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, FILE_WRITE_DATA) + fsctl_set_zero_data = (file_device_file_system << 16) | (file_write_data << 14) | (50 << 2) | method_buffered + ) + retPtr := uintptr(unsafe.Pointer(&(make([]byte, 8)[0]))) + var r1 uintptr + var e1 syscall.Errno + if setSparse { + // BOOL + // WINAPI + // DeviceIoControl( (HANDLE) hDevice, // handle to a file + // FSCTL_SET_SPARSE, // dwIoControlCode + // (PFILE_SET_SPARSE_BUFFER) lpInBuffer, // input buffer + // (DWORD) nInBufferSize, // size of input buffer + // NULL, // lpOutBuffer + // 0, // nOutBufferSize + // (LPDWORD) lpBytesReturned, // number of bytes returned + // (LPOVERLAPPED) lpOverlapped ); // OVERLAPPED structure + r1, _, e1 = syscall.Syscall9(procDeviceIOControl.Addr(), 8, + fd, + uintptr(fsctl_set_sparse), + // If the lpInBuffer parameter is NULL, the operation will behave the same as if the SetSparse member of the FILE_SET_SPARSE_BUFFER structure were TRUE. In other words, the operation sets the file to a sparse file. + 0, // uintptr(unsafe.Pointer(&lpInBuffer)), + 0, // 1, + 0, + 0, + retPtr, + 0, + 0) + } else { + // BOOL + // WINAPI + // DeviceIoControl( (HANDLE) hDevice, // handle to a file + // FSCTL_SET_ZERO_DATA, // dwIoControlCode + // (LPVOID) lpInBuffer, // input buffer + // (DWORD) nInBufferSize, // size of input buffer + // NULL, // lpOutBuffer + // 0, // nOutBufferSize + // (LPDWORD) lpBytesReturned, // number of bytes returned + // (LPOVERLAPPED) lpOverlapped ); // OVERLAPPED structure + r1, _, e1 = syscall.Syscall9(procDeviceIOControl.Addr(), 8, + fd, + uintptr(fsctl_set_zero_data), + inBuf, + inBufLen, + 0, + 0, + retPtr, + 0, + 0) + } + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return err +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/LICENSE b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/LICENSE new file mode 100644 index 000000000..1e92e33dd --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of CZ.NIC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/README b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/README new file mode 100644 index 000000000..05ce89f92 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/README @@ -0,0 +1,5 @@ +This is a goinstall-able mirror of modified code already published at: +https://git.nic.cz/redmine/projects/gofileutil/repository/show/hdb + +Install: $go get github.com/cznic/fileutil/hdb +Godocs: http://gopkgdoc.appspot.com/pkg/github.com/cznic/fileutil/hdb diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/all_test.go b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/all_test.go new file mode 100644 index 000000000..a980587f1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/all_test.go @@ -0,0 +1,15 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package hdb + +import ( + "testing" +) + +func TestPlaceholder(t *testing.T) { + t.Log("TODO") //TODO +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/hdb.go b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/hdb.go new file mode 100644 index 000000000..7c7113d9d --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/hdb.go @@ -0,0 +1,153 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +/* +WIP: Package hdb provides a "handle"/value DB like store, but actually it's +closer to the model of a process's virtual memory and its alloc, free and move +methods. + +The hdb package is a thin layer around falloc.File providing stable-only +handles and the basic synchronizing primitives. The central functionality of +hdb are the New, Set, Get and Delete methods of Store. + +Conceptual analogy: + New alloc(sizeof(content)), return new "memory" pointer (a handle). + + Get memmove() from "memory" "pointed to" by handle to the result content. + Note: Handle "knows" the size of its content. + + Set memmove() from content to "memory" pointed to by handle. + In contrast to real memory, the new content may have different + size than the previously stored one w/o additional handling + and the "pointer" handle remains the same. + + Delete free() the "memory" "pointed to" by handle. +*/ +package hdb + +import ( + "github.com/cznic/fileutil/falloc" + "github.com/cznic/fileutil/storage" +) + +type Store struct { + f *falloc.File +} + +// New returns a newly created Store backed by accessor, discarding its conents if any. +// If successful, methods on the returned Store can be used for I/O. +// It returns the Store and an error, if any. +func New(accessor storage.Accessor) (store *Store, err error) { + s := &Store{} + if s.f, err = falloc.New(accessor); err == nil { + store = s + } + return +} + +// Open opens the Store from accessor. +// If successful, methods on the returned Store can be used for data exchange. +// It returns the Store and an error, if any. +func Open(accessor storage.Accessor) (store *Store, err error) { + s := &Store{} + if s.f, err = falloc.Open(accessor); err == nil { + store = s + } + return +} + +// Close closes the store. Further access to the store has undefined behavior and may panic. +// It returns an error, if any. +func (s *Store) Close() (err error) { + defer func() { + s.f = nil + }() + + return s.f.Close() +} + +// Delete deletes the data associated with handle. +// It returns an error if any. +func (s *Store) Delete(handle falloc.Handle) (err error) { + return s.f.Free(handle) +} + +// Get gets the data associated with handle. +// It returns the data and an error, if any. +func (s *Store) Get(handle falloc.Handle) (b []byte, err error) { + return s.f.Read(handle) +} + +// New associates data with a new handle. +// It returns the handle and an error, if any. +func (s *Store) New(b []byte) (handle falloc.Handle, err error) { + return s.f.Alloc(b) +} + +// Set associates data with an existing handle. +// It returns an error, if any. +func (s *Store) Set(handle falloc.Handle, b []byte) (err error) { + _, err = s.f.Realloc(handle, b, true) + return +} + +// Root returns the handle of the DB root (top level directory, ...). +func (s *Store) Root() falloc.Handle { + return s.f.Root() +} + +// File returns the underlying falloc.File of 's'. +func (s *Store) File() *falloc.File { + return s.f +} + +// Lock locks 's' for writing. If the lock is already locked for reading or writing, +// Lock blocks until the lock is available. To ensure that the lock eventually becomes available, +// a blocked Lock call excludes new readers from acquiring the lock. +func (s *Store) Lock() { + s.f.Lock() +} + +// RLock locks 's' for reading. If the lock is already locked for writing or there is a writer +// already waiting to release the lock, RLock blocks until the writer has released the lock. +func (s *Store) RLock() { + s.f.RLock() +} + +// Unlock unlocks 's' for writing. It's a run-time error if 's' is not locked for writing on entry to Unlock. +// +// As with Mutexes, a locked RWMutex is not associated with a particular goroutine. +// One goroutine may RLock (Lock) 's' and then arrange for another goroutine to RUnlock (Unlock) it. +func (s *Store) Unlock() { + s.f.Unlock() +} + +// RUnlock undoes a single RLock call; it does not affect other simultaneous readers. +// It's a run-time error if 's' is not locked for reading on entry to RUnlock. +func (s *Store) RUnlock() { + s.f.RUnlock() +} + +// LockedNew wraps New in a Lock/Unlock pair. +func (s *Store) LockedNew(b []byte) (handle falloc.Handle, err error) { + return s.f.LockedAlloc(b) +} + +// LockedDelete wraps Delete in a Lock/Unlock pair. +func (s *Store) LockedDelete(handle falloc.Handle) (err error) { + return s.f.LockedFree(handle) +} + +// LockedGet wraps Get in a RLock/RUnlock pair. +func (s *Store) LockedGet(handle falloc.Handle) (b []byte, err error) { + return s.f.LockedRead(handle) +} + +// LockedSet wraps Set in a Lock/Unlock pair. +func (s *Store) LockedSet(handle falloc.Handle, b []byte) (err error) { + _, err = s.f.Realloc(handle, b, true) + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/test_deps.go b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/test_deps.go new file mode 100644 index 000000000..3164f63ae --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/hdb/test_deps.go @@ -0,0 +1,13 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package hdb + +// Pull test dependencies too. +// Enables easy 'go test X' after 'go get X' +import ( +// nothing yet +) diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/punch_test.go b/Godeps/_workspace/src/github.com/cznic/fileutil/punch_test.go new file mode 100644 index 000000000..766f6f4ba --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/punch_test.go @@ -0,0 +1,55 @@ +// Copyright (c) 2014 The fileutil authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fileutil + +import ( + "io/ioutil" + "os" + "testing" +) + +func TestPunch(t *testing.T) { + file, err := ioutil.TempFile("", "punchhole-") + if err != nil { + t.Error(err) + } + defer os.Remove(file.Name()) + defer file.Close() + buf := make([]byte, 10<<20) + for i := range buf { + buf[i] = byte(1 + (i+1)&0xfe) + } + if _, err = file.Write(buf); err != nil { + t.Errorf("error writing to the temp file: %v", err) + t.FailNow() + } + if err = file.Sync(); err != nil { + t.Logf("error syncing %q: %v", file.Name(), err) + } + for i, j := range []int{1, 31, 1 << 10} { + if err = PunchHole(file, int64(j), int64(j)); err != nil { + t.Errorf("%d. error punching at %d, size %d: %v", i, j, j, err) + continue + } + // read back, with 1-1 bytes overlaid + n, err := file.ReadAt(buf[:j+2], int64(j-1)) + if err != nil { + t.Errorf("%d. error reading file: %v", i, err) + continue + } + buf = buf[:n] + if buf[0] == 0 { + t.Errorf("%d. file at %d has been overwritten with 0!", i, j-1) + } + if buf[n-1] == 0 { + t.Errorf("%d. file at %d has been overwritten with 0!", i, j-1+n) + } + for k, v := range buf[1 : n-1] { + if v != 0 { + t.Errorf("%d. error reading file at %d got %d, want 0.", i, k, v) + } + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/LICENSE b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/LICENSE new file mode 100644 index 000000000..1e92e33dd --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of CZ.NIC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/README b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/README new file mode 100644 index 000000000..2a400fced --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/README @@ -0,0 +1,5 @@ +This is a goinstall-able mirror of modified code already published at: +https://git.nic.cz/redmine/projects/gofileutil/repository/show/storage + +Install: $go get github.com/cznic/fileutil/storage +Godocs: http://gopkgdoc.appspot.com/pkg/github.com/cznic/fileutil/storage diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/all_test.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/all_test.go new file mode 100644 index 000000000..8947a0a72 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/all_test.go @@ -0,0 +1,22 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +import ( + "flag" + "runtime" +) + +var ( + devFlag = flag.Bool("dev", false, "enable dev tests") + goFlag = flag.Int("go", 1, "GOMAXPROCS") +) + +func init() { + flag.Parse() + runtime.GOMAXPROCS(*goFlag) +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/cache.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/cache.go new file mode 100644 index 000000000..3a6115a71 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/cache.go @@ -0,0 +1,322 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +import ( + "container/list" + "io" + "math" + "os" + "sync" + "sync/atomic" +) + +type cachepage struct { + b [512]byte + dirty bool + lru *list.Element + pi int64 + valid int // page content is b[:valid] +} + +func (p *cachepage) wr(b []byte, off int) (wasDirty bool) { + copy(p.b[off:], b) + if n := off + len(b); n > p.valid { + p.valid = n + } + wasDirty = p.dirty + p.dirty = true + return +} + +func (c *Cache) rd(off int64, read bool) (p *cachepage, ok bool) { + c.Rq++ + pi := off >> 9 + if p, ok = c.m[pi]; ok { + c.lru.MoveToBack(p.lru) + return + } + + if !read { + return + } + + fp := off &^ 511 + if fp >= c.size { + return + } + + rq := 512 + if fp+512 > c.size { + rq = int(c.size - fp) + } + p = &cachepage{pi: pi, valid: rq} + p.lru = c.lru.PushBack(p) + if n, err := c.f.ReadAt(p.b[:p.valid], fp); n != rq { + panic(err) + } + + c.Load++ + if c.advise != nil { + c.advise(fp, 512, false) + } + c.m[pi], ok = p, true + return +} + +func (c *Cache) wr(off int64) (p *cachepage) { + var ok bool + if p, ok = c.rd(off, false); ok { + return + } + + pi := off >> 9 + p = &cachepage{pi: pi} + p.lru = c.lru.PushBack(p) + c.m[pi] = p + return +} + +// Cache provides caching support for another store Accessor. +type Cache struct { + advise func(int64, int, bool) + clean chan bool + cleaning int32 + close chan bool + f Accessor + fi *FileInfo + lock sync.Mutex + lru *list.List + m map[int64]*cachepage + maxpages int + size int64 + sync chan bool + wlist *list.List + write chan bool + writing int32 + Rq int64 // Pages requested from cache + Load int64 // Pages loaded (cache miss) + Purge int64 // Pages purged + Top int // "High water" pages +} + +// Implementation of Accessor. +func (c *Cache) BeginUpdate() error { return nil } + +// Implementation of Accessor. +func (c *Cache) EndUpdate() error { return nil } + +// NewCache creates a caching Accessor from store with total of maxcache bytes. +// NewCache returns the new Cache, implementing Accessor or an error if any. +// +// The LRU mechanism is used, so the cache tries to keep often accessed pages cached. +// +func NewCache(store Accessor, maxcache int64, advise func(int64, int, bool)) (c *Cache, err error) { + var fi os.FileInfo + if fi, err = store.Stat(); err != nil { + return + } + + x := maxcache >> 9 + if x > math.MaxInt32/2 { + x = math.MaxInt32 / 2 + } + c = &Cache{ + advise: advise, + clean: make(chan bool, 1), + close: make(chan bool), + f: store, + lru: list.New(), // front == oldest used, back == last recently used + m: make(map[int64]*cachepage), + maxpages: int(x), + size: fi.Size(), + sync: make(chan bool), + wlist: list.New(), + write: make(chan bool, 1), + } + c.fi = NewFileInfo(fi, c) + go c.writer() + go c.cleaner(int((int64(c.maxpages) * 95) / 100)) // hysteresis + return +} + +func (c *Cache) Accessor() Accessor { + return c.f +} + +func (c *Cache) Close() (err error) { + close(c.write) + <-c.close + close(c.clean) + <-c.close + return c.f.Close() +} + +func (c *Cache) Name() (s string) { + return c.f.Name() +} + +func (c *Cache) ReadAt(b []byte, off int64) (n int, err error) { + po := int(off) & 0x1ff + bp := 0 + rem := len(b) + m := 0 + for rem != 0 { + c.lock.Lock() // X1+ + p, ok := c.rd(off, true) + if !ok { + c.lock.Unlock() // X1- + return -1, io.EOF + } + + rq := rem + if po+rq > 512 { + rq = 512 - po + } + if n := copy(b[bp:bp+rq], p.b[po:p.valid]); n != rq { + c.lock.Unlock() // X1- + return -1, io.EOF + } + + m = len(c.m) + c.lock.Unlock() // X1- + po = 0 + bp += rq + off += int64(rq) + rem -= rq + n += rq + } + if m > c.maxpages && atomic.CompareAndSwapInt32(&c.cleaning, 0, 1) { + if m > c.Top { + c.Top = m + } + c.clean <- true + } + return +} + +func (c *Cache) Stat() (fi os.FileInfo, err error) { + c.lock.Lock() + defer c.lock.Unlock() + return c.fi, nil +} + +func (c *Cache) Sync() (err error) { + c.write <- false + <-c.sync + return +} + +func (c *Cache) Truncate(size int64) (err error) { + c.Sync() //TODO improve (discard pages, the writer goroutine should also be aware, ...) + c.lock.Lock() + defer c.lock.Unlock() + c.size = size + return c.f.Truncate(size) +} + +func (c *Cache) WriteAt(b []byte, off int64) (n int, err error) { + po := int(off) & 0x1ff + bp := 0 + rem := len(b) + m := 0 + for rem != 0 { + c.lock.Lock() // X+ + p := c.wr(off) + rq := rem + if po+rq > 512 { + rq = 512 - po + } + if wasDirty := p.wr(b[bp:bp+rq], po); !wasDirty { + c.wlist.PushBack(p) + } + m = len(c.m) + po = 0 + bp += rq + off += int64(rq) + if off > c.size { + c.size = off + } + c.lock.Unlock() // X- + rem -= rq + n += rq + } + if atomic.CompareAndSwapInt32(&c.writing, 0, 1) { + c.write <- true + } + if m > c.maxpages && atomic.CompareAndSwapInt32(&c.cleaning, 0, 1) { + if m > c.Top { + c.Top = m + } + c.clean <- true + } + return +} + +func (c *Cache) writer() { + for ok := true; ok; { + var wr bool + var off int64 + wr, ok = <-c.write + for { + c.lock.Lock() // X1+ + item := c.wlist.Front() + if item == nil { + c.lock.Unlock() // X1- + break + } + + p := item.Value.(*cachepage) + off = p.pi << 9 + if n, err := c.f.WriteAt(p.b[:p.valid], off); n != p.valid { + c.lock.Unlock() // X1- + panic("TODO Cache.writer errchan") //TODO +errchan + panic(err) + } + + p.dirty = false + c.wlist.Remove(item) + if c.advise != nil { + c.advise(off, 512, true) + } + c.lock.Unlock() // X1- + } + switch { + case wr: + atomic.AddInt32(&c.writing, -1) + case ok: + c.sync <- true + } + } + c.close <- true +} + +func (c *Cache) cleaner(limit int) { + for _ = range c.clean { + var item *list.Element + for { + c.lock.Lock() // X1+ + if len(c.m) < limit { + c.lock.Unlock() // X1- + break + } + + if item == nil { + item = c.lru.Front() + } + if p := item.Value.(*cachepage); !p.dirty { + delete(c.m, p.pi) + c.lru.Remove(item) + c.Purge++ + } + item = item.Next() + c.lock.Unlock() // X1- + } + atomic.AddInt32(&c.cleaning, -1) + } + c.close <- true +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/cache_test.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/cache_test.go new file mode 100644 index 000000000..8dbad0887 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/cache_test.go @@ -0,0 +1,83 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func newfile(t *testing.T) (string, string, Accessor) { + dir, err := ioutil.TempDir("", "test-storage-") + if err != nil { + panic(err) + } + + name := filepath.Join(dir, "test.tmp") + f, err := NewFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666) + if err != nil { + t.Fatal("newfile", err) + } + + return dir, name, f +} + +func readfile(t *testing.T, name string) (b []byte) { + var err error + if b, err = ioutil.ReadFile(name); err != nil { + t.Fatal("readfile") + } + + return +} + +func newcache(t *testing.T) (dir, name string, c *Cache) { + dir, name, f := newfile(t) + var err error + if c, err = NewCache(f, 1<<20, nil); err != nil { + t.Fatal("newCache", err) + } + + return +} + +func TestCache0(t *testing.T) { + dir, name, c := newcache(t) + defer os.RemoveAll(dir) + + if err := c.Close(); err != nil { + t.Fatal(10, err) + } + + if b := readfile(t, name); len(b) != 0 { + t.Fatal(20, len(b), 0) + } +} + +func TestCache1(t *testing.T) { + dir, name, c := newcache(t) + defer os.RemoveAll(dir) + + if n, err := c.WriteAt([]byte{0xa5}, 0); n != 1 { + t.Fatal(20, n, err) + } + + if err := c.Close(); err != nil { + t.Fatal(10, err) + } + + b := readfile(t, name) + if len(b) != 1 { + t.Fatal(30, len(b), 1) + } + + if b[0] != 0xa5 { + t.Fatal(40, b[0], 0xa5) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/dev_test.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/dev_test.go new file mode 100644 index 000000000..7287a27ae --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/dev_test.go @@ -0,0 +1,18 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +import ( + "testing" +) + +func TestDevNothing(t *testing.T) { + if !*devFlag { + t.Log("not enabled") + return + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/file.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/file.go new file mode 100644 index 000000000..94feda5ef --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/file.go @@ -0,0 +1,50 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +import ( + "os" +) + +// FileAccessor is the concrete type returned by NewFile and OpenFile. +type FileAccessor struct { + *os.File +} + +// Implementation of Accessor. +func (f *FileAccessor) BeginUpdate() error { return nil } + +// Implementation of Accessor. +func (f *FileAccessor) EndUpdate() error { return nil } + +// NewFile returns an Accessor backed by an os.File named name, It opens the +// named file with specified flag (os.O_RDWR etc.) and perm, (0666 etc.) if +// applicable. If successful, methods on the returned Accessor can be used for +// I/O. It returns the Accessor and an Error, if any. +// +// NOTE: The returned Accessor implements BeginUpdate and EndUpdate as a no op. +func NewFile(name string, flag int, perm os.FileMode) (store Accessor, err error) { + var f FileAccessor + if f.File, err = os.OpenFile(name, flag, perm); err == nil { + store = &f + } + return +} + +// OpenFile returns an Accessor backed by an existing os.File named name, It +// opens the named file with specified flag (os.O_RDWR etc.) and perm, (0666 +// etc.) if applicable. If successful, methods on the returned Accessor can be +// used for I/O. It returns the Accessor and an Error, if any. +// +// NOTE: The returned Accessor implements BeginUpdate and EndUpdate as a no op. +func OpenFile(name string, flag int, perm os.FileMode) (store Accessor, err error) { + var f FileAccessor + if f.File, err = os.OpenFile(name, flag, perm); err == nil { + store = &f + } + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/mem.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/mem.go new file mode 100644 index 000000000..7cda0b667 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/mem.go @@ -0,0 +1,161 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +import ( + "errors" + "fmt" + "io/ioutil" + "math" + "os" +) + +//TODO -> exported type w/ exported fields +type memaccessor struct { + f *os.File + fi *FileInfo + b []byte +} + +// Implementation of Accessor. +func (m *memaccessor) BeginUpdate() error { return nil } + +// Implementation of Accessor. +func (f *memaccessor) EndUpdate() error { return nil } + +// NewMem returns a new Accessor backed by an os.File. The returned Accessor +// keeps all of the store content in memory. The memory and file images are +// synced only by Sync and Close. Recomended for small amounts of data only +// and content which may be lost on process kill/crash. NewMem return the +// Accessor or an error of any. +// +// NOTE: The returned Accessor implements BeginUpdate and EndUpdate as a no op. +func NewMem(f *os.File) (store Accessor, err error) { + a := &memaccessor{f: f} + if err = f.Truncate(0); err != nil { + return + } + + var fi os.FileInfo + if fi, err = a.f.Stat(); err != nil { + return + } + + a.fi = NewFileInfo(fi, a) + store = a + return +} + +// OpenMem return a new Accessor backed by an os.File. The store content is +// loaded from f. The returned Accessor keeps all of the store content in +// memory. The memory and file images are synced only Sync and Close. +// Recomended for small amounts of data only and content which may be lost on +// process kill/crash. OpenMem return the Accessor or an error of any. +// +// NOTE: The returned Accessor implements BeginUpdate and EndUpdate as a no op. +func OpenMem(f *os.File) (store Accessor, err error) { + a := &memaccessor{f: f} + if a.b, err = ioutil.ReadAll(a.f); err != nil { + a.f.Close() + return + } + + var fi os.FileInfo + if fi, err = a.f.Stat(); err != nil { + a.f.Close() + return + } + + a.fi = NewFileInfo(fi, a) + store = a + return +} + +// Close implements Accessor. Specifically it synchronizes the memory and file images. +func (a *memaccessor) Close() (err error) { + defer func() { + a.b = nil + if a.f != nil { + if e := a.f.Close(); e != nil && err == nil { + err = e + } + } + a.f = nil + }() + + return a.Sync() +} + +func (a *memaccessor) Name() string { + return a.f.Name() +} + +func (a *memaccessor) ReadAt(b []byte, off int64) (n int, err error) { + if off < 0 || off > math.MaxInt32 { + return -1, fmt.Errorf("ReadAt: illegal offset %#x", off) + } + + rq, fp := len(b), int(off) + if fp+rq > len(a.b) { + return -1, fmt.Errorf("ReadAt: illegal rq %#x @ offset %#x, len %#x", rq, fp, len(a.b)) + } + + copy(b, a.b[fp:]) + return +} + +func (a *memaccessor) Stat() (fi os.FileInfo, err error) { + i := a.fi + i.FSize = int64(len(a.b)) + fi = i + return +} + +// Sync implements Accessor. Specifically it synchronizes the memory and file images. +func (a *memaccessor) Sync() (err error) { + var n int + if n, err = a.f.WriteAt(a.b, 0); n != len(a.b) { + return + } + + return a.f.Truncate(int64(len(a.b))) +} + +func (a *memaccessor) Truncate(size int64) (err error) { + defer func() { + if e := recover(); e != nil { + err = e.(error) + } + }() + + if size > math.MaxInt32 { + panic(errors.New("truncate: illegal size")) + } + + a.b = a.b[:int(size)] + return +} + +func (a *memaccessor) WriteAt(b []byte, off int64) (n int, err error) { + if off < 0 || off > math.MaxInt32 { + return -1, errors.New("WriteAt: illegal offset") + } + + rq, fp, size := len(b), int(off), len(a.b) + if need := rq + fp; need > size { + if need <= cap(a.b) { + a.b = a.b[:need] + } else { + nb := make([]byte, need, 2*need) + copy(nb, a.b) + a.b = nb + } + } + + copy(a.b[int(off):], b) + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/mem_test.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/mem_test.go new file mode 100644 index 000000000..921948c6e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/mem_test.go @@ -0,0 +1,15 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +import ( + "testing" +) + +func Test(t *testing.T) { + t.Log("TODO placeholder") //TODO +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/probe.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/probe.go new file mode 100644 index 000000000..53b146a6e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/probe.go @@ -0,0 +1,74 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +import "sync/atomic" + +// Probe collects usage statistics of the embeded Accessor. +// Probe itself IS an Accessor. +type Probe struct { + Accessor + Chain *Probe + OpsRd int64 + OpsWr int64 + BytesRd int64 + BytesWr int64 + SectorsRd int64 // Assuming 512 byte sector size + SectorsWr int64 +} + +// NewProbe returns a newly created probe which embedes the src Accessor. +// The retuned *Probe satisfies Accessor. if chain != nil then Reset() +// is cascaded down the chained Probes. +func NewProbe(src Accessor, chain *Probe) *Probe { + return &Probe{Accessor: src, Chain: chain} +} + +func reset(n *int64) { + atomic.AddInt64(n, -atomic.AddInt64(n, 0)) +} + +// Reset zeroes the collected statistics of p. +func (p *Probe) Reset() { + if p.Chain != nil { + p.Chain.Reset() + } + reset(&p.OpsRd) + reset(&p.OpsWr) + reset(&p.BytesRd) + reset(&p.BytesWr) + reset(&p.SectorsRd) + reset(&p.SectorsWr) +} + +func (p *Probe) ReadAt(b []byte, off int64) (n int, err error) { + n, err = p.Accessor.ReadAt(b, off) + atomic.AddInt64(&p.OpsRd, 1) + atomic.AddInt64(&p.BytesRd, int64(n)) + if n <= 0 { + return + } + + sectorFirst := off >> 9 + sectorLast := (off + int64(n) - 1) >> 9 + atomic.AddInt64(&p.SectorsRd, sectorLast-sectorFirst+1) + return +} + +func (p *Probe) WriteAt(b []byte, off int64) (n int, err error) { + n, err = p.Accessor.WriteAt(b, off) + atomic.AddInt64(&p.OpsWr, 1) + atomic.AddInt64(&p.BytesWr, int64(n)) + if n <= 0 { + return + } + + sectorFirst := off >> 9 + sectorLast := (off + int64(n) - 1) >> 9 + atomic.AddInt64(&p.SectorsWr, sectorLast-sectorFirst+1) + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/probe_test.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/probe_test.go new file mode 100644 index 000000000..00eca8ee5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/probe_test.go @@ -0,0 +1,86 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +import ( + "os" + "testing" +) + +func (p *Probe) assert(t *testing.T, msg int, opsRd, opsWr, bytesRd, bytesWr, sectorsRd, sectorsWr int64) { + if n := p.OpsRd; n != opsRd { + t.Fatal(msg, n, opsRd) + } + + if n := p.OpsWr; n != opsWr { + t.Fatal(msg+1, n, opsWr) + } + + if n := p.BytesRd; n != bytesRd { + t.Fatal(msg+2, n, bytesRd) + } + + if n := p.BytesWr; n != bytesWr { + t.Fatal(msg+3, n, bytesWr) + } + + if n := p.SectorsRd; n != sectorsRd { + t.Fatal(msg+4, n, sectorsRd) + } + + if n := p.SectorsWr; n != sectorsWr { + t.Fatal(msg+5, n, sectorsWr) + } +} + +func TestProbe(t *testing.T) { + return //TODO disabled due to atomic.AddInt64 failing on W32 + const fn = "test.tmp" + + store, err := NewFile(fn, os.O_CREATE|os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + t.Fatal(10, err) + } + + defer func() { + ec := store.Close() + er := os.Remove(fn) + if ec != nil { + t.Fatal(10000, ec) + } + if er != nil { + t.Fatal(10001, er) + } + }() + + probe := NewProbe(store, nil) + if n, err := probe.WriteAt([]byte{1}, 0); n != 1 { + t.Fatal(20, err) + } + + probe.assert(t, 30, 0, 1, 0, 1, 0, 1) + b := []byte{0} + if n, err := probe.ReadAt(b, 0); n != 1 { + t.Fatal(40, err) + } + + if n := b[0]; n != 1 { + t.Fatal(50, n, 1) + } + + probe.assert(t, 60, 1, 1, 1, 1, 1, 1) + if n, err := probe.WriteAt([]byte{2, 3}, 510); n != 2 { + t.Fatal(70, err) + } + + probe.assert(t, 80, 1, 2, 1, 3, 1, 2) + if n, err := probe.WriteAt([]byte{2, 3}, 511); n != 2 { + t.Fatal(90, err) + } + + probe.assert(t, 100, 1, 3, 1, 5, 1, 4) +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/storage.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/storage.go new file mode 100644 index 000000000..4956053a0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/storage.go @@ -0,0 +1,141 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +// WIP: Package storage defines and implements storage providers and store accessors. +package storage + +import ( + "os" + "sync" + "time" +) + +// FileInfo is a type implementing os.FileInfo which has setable fields, like +// the older os.FileInfo used to have. It is used wehere e.g. the Size is +// needed to be faked (encapsulated/memory only file, file cache, etc.). +type FileInfo struct { + FName string // base name of the file + FSize int64 // length in bytes + FMode os.FileMode // file mode bits + FModTime time.Time // modification time + FIsDir bool // abbreviation for Mode().IsDir() + sys interface{} // underlying data source (can be nil) +} + +// NewFileInfo creates FileInfo from os.FileInfo fi. +func NewFileInfo(fi os.FileInfo, sys interface{}) *FileInfo { + return &FileInfo{fi.Name(), fi.Size(), fi.Mode(), fi.ModTime(), fi.IsDir(), sys} +} + +// Implementation of os.FileInfo +func (fi *FileInfo) Name() string { + return fi.FName +} + +// Implementation of os.FileInfo +func (fi *FileInfo) Size() int64 { + return fi.FSize +} + +// Implementation of os.FileInfo +func (fi *FileInfo) Mode() os.FileMode { + return fi.FMode +} + +// Implementation of os.FileInfo +func (fi *FileInfo) ModTime() time.Time { + return fi.FModTime +} + +// Implementation of os.FileInfo +func (fi *FileInfo) IsDir() bool { + return fi.FIsDir +} + +func (fi *FileInfo) Sys() interface{} { + return fi.sys +} + +// Accessor provides I/O methods to access a store. +type Accessor interface { + + // Close closes the store, rendering it unusable for I/O. It returns an + // error, if any. + Close() error + + // Name returns the name of the file as presented to Open. + Name() string + + // ReadAt reads len(b) bytes from the store starting at byte offset off. + // It returns the number of bytes read and the error, if any. + // EOF is signaled by a zero count with err set to os.EOF. + // ReadAt always returns a non-nil Error when n != len(b). + ReadAt(b []byte, off int64) (n int, err error) + + // Stat returns the FileInfo structure describing the store. It returns + // the os.FileInfo and an error, if any. + Stat() (fi os.FileInfo, err error) + + // Sync commits the current contents of the store to stable storage. + // Typically, this means flushing the file system's in-memory copy of + // recently written data to disk. + Sync() (err error) + + // Truncate changes the size of the store. It does not change the I/O + // offset. + Truncate(size int64) error + + // WriteAt writes len(b) bytes to the store starting at byte offset off. + // It returns the number of bytes written and an error, if any. + // WriteAt returns a non-nil Error when n != len(b). + WriteAt(b []byte, off int64) (n int, err error) + + // Before every [structural] change of a store the BeginUpdate is to be + // called and paired with EndUpdate after the change makes the store's + // state consistent again. Invocations of BeginUpdate may nest. On + // invoking the last non nested EndUpdate an implicit "commit" should + // be performed by the store/provider. The concrete mechanism is + // unspecified. It could be for example a write-ahead log. Stores may + // implement BeginUpdate and EndUpdate as a (documented) no op. + BeginUpdate() error + EndUpdate() error +} + +// Mutate is a helper/wrapper for executing f in between a.BeginUpdate and +// a.EndUpdate. Any parameters and/or return values except an error should be +// captured by a function literal passed as f. The returned err is either nil +// or the first non nil error returned from the sequence of execution: +// BeginUpdate, [f,] EndUpdate. The pair BeginUpdate/EndUpdate *is* invoked +// always regardles of any possible errors produced. Mutate doesn't handle +// panic, it should be used only with a function [literal] which doesn't panic. +// Otherwise the pairing of BeginUpdate/EndUpdate is not guaranteed. +// +// NOTE: If BeginUpdate, which is invoked before f, returns a non-nil error, +// then f is not invoked at all (but EndUpdate still is). +func Mutate(a Accessor, f func() error) (err error) { + defer func() { + if e := a.EndUpdate(); e != nil && err == nil { + err = e + } + }() + + if err = a.BeginUpdate(); err != nil { + return + } + + return f() +} + +// LockedMutate wraps Mutate in yet another layer consisting of a +// l.Lock/l.Unlock pair. All other limitations apply as in Mutate, e.g. no +// panics are allowed to happen - otherwise no guarantees can be made about +// Unlock matching the Lock. +func LockedMutate(a Accessor, l sync.Locker, f func() error) (err error) { + l.Lock() + defer l.Unlock() + + return Mutate(a, f) +} diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/storage/test_deps.go b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/test_deps.go new file mode 100644 index 000000000..92ac44a5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/storage/test_deps.go @@ -0,0 +1,13 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package storage + +// Pull test dependencies too. +// Enables easy 'go test X' after 'go get X' +import ( +// nothing yet +) diff --git a/Godeps/_workspace/src/github.com/cznic/fileutil/test_deps.go b/Godeps/_workspace/src/github.com/cznic/fileutil/test_deps.go new file mode 100644 index 000000000..eec608ab3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/fileutil/test_deps.go @@ -0,0 +1,13 @@ +// Copyright (c) 2014 The fileutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +package fileutil + +// Pull test dependencies too. +// Enables easy 'go test X' after 'go get X' +import ( +// nothing yet +) diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/AUTHORS b/Godeps/_workspace/src/github.com/cznic/mathutil/AUTHORS new file mode 100644 index 000000000..d04c45011 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/AUTHORS @@ -0,0 +1,12 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +CZ.NIC z.s.p.o. +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/mathutil/CONTRIBUTORS new file mode 100644 index 000000000..9c9a5dd84 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/CONTRIBUTORS @@ -0,0 +1,10 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Gary Burd +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/LICENSE b/Godeps/_workspace/src/github.com/cznic/mathutil/LICENSE new file mode 100644 index 000000000..128a1b64a --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The mathutil Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/Makefile b/Godeps/_workspace/src/github.com/cznic/mathutil/Makefile new file mode 100644 index 000000000..b99ba9adf --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/Makefile @@ -0,0 +1,31 @@ +# Copyright (c) 2014 The mathutil Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all todo clean nuke + +grep=--include=*.go --include=*.run --include=*.y + +all: editor + go build + go vet || true + golint . + go install + make todo + +clean: + go clean + +editor: + go fmt + go test -i + go test + +todo: + @grep -nr $(grep) ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* * || true + @grep -nr $(grep) TODO * || true + @grep -nr $(grep) BUG * || true + @grep -nr $(grep) println * || true + +nuke: clean + go clean -i diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/README b/Godeps/_workspace/src/github.com/cznic/mathutil/README new file mode 100644 index 000000000..a9ee59c40 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/README @@ -0,0 +1,10 @@ +This is a goinstall-able mirror of modified code already published at: +http://git.nic.cz/redmine/projects/gornd/repository + +Packages in this repository: + +Install: $ go get github.com/cznic/mathutil +Godocs: http://godoc.org/github.com/cznic/mathutil + +Install: $ go get github.com/cznic/mathutil/mersenne +Godocs: http://godoc.org/github.com/cznic/mathutil/mersenne diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/all_test.go b/Godeps/_workspace/src/github.com/cznic/mathutil/all_test.go new file mode 100644 index 000000000..38427ad4f --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/all_test.go @@ -0,0 +1,3530 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mathutil + +import ( + "fmt" + "math" + "math/big" + "math/rand" + "os" + "path" + "runtime" + "sort" + "strings" + "sync" + "testing" +) + +func caller(s string, va ...interface{}) { + _, fn, fl, _ := runtime.Caller(2) + fmt.Fprintf(os.Stderr, "caller: %s:%d: ", path.Base(fn), fl) + fmt.Fprintf(os.Stderr, s, va...) + fmt.Fprintln(os.Stderr) + _, fn, fl, _ = runtime.Caller(1) + fmt.Fprintf(os.Stderr, "\tcallee: %s:%d: ", path.Base(fn), fl) + fmt.Fprintln(os.Stderr) +} + +func dbg(s string, va ...interface{}) { + if s == "" { + s = strings.Repeat("%v ", len(va)) + } + _, fn, fl, _ := runtime.Caller(1) + fmt.Fprintf(os.Stderr, "dbg %s:%d: ", path.Base(fn), fl) + fmt.Fprintf(os.Stderr, s, va...) + fmt.Fprintln(os.Stderr) +} + +func TODO(...interface{}) string { + _, fn, fl, _ := runtime.Caller(1) + return fmt.Sprintf("TODO: %s:%d:\n", path.Base(fn), fl) +} + +func use(...interface{}) {} + +// ============================================================================ + +func r32() *FC32 { + r, err := NewFC32(math.MinInt32, math.MaxInt32, true) + if err != nil { + panic(err) + } + + return r +} + +var ( + r64lo = big.NewInt(math.MinInt64) + r64hi = big.NewInt(math.MaxInt64) + _3 = big.NewInt(3) + MinIntM1 = MinInt + MaxIntP1 = MaxInt + MaxUintP1 uint = MaxUint +) + +func init() { + MinIntM1-- + MaxIntP1++ + MaxUintP1++ +} + +func r64() *FCBig { + r, err := NewFCBig(r64lo, r64hi, true) + if err != nil { + panic(err) + } + + return r +} + +func benchmark1eN(b *testing.B, r *FC32) { + b.StartTimer() + for i := 0; i < b.N; i++ { + r.Next() + } +} + +func BenchmarkFC1e3(b *testing.B) { + b.StopTimer() + r, _ := NewFC32(0, 1e3, false) + benchmark1eN(b, r) +} + +func BenchmarkFC1e6(b *testing.B) { + b.StopTimer() + r, _ := NewFC32(0, 1e6, false) + benchmark1eN(b, r) +} + +func BenchmarkFC1e9(b *testing.B) { + b.StopTimer() + r, _ := NewFC32(0, 1e9, false) + benchmark1eN(b, r) +} + +func Test0(t *testing.T) { + const N = 10000 + for n := 1; n < N; n++ { + lo, hi := 0, n-1 + period := int64(hi) - int64(lo) + 1 + r, err := NewFC32(lo, hi, false) + if err != nil { + t.Fatal(err) + } + if r.Cycle()-period > period { + t.Fatalf("Cycle exceeds 2 * period") + } + } + for n := 1; n < N; n++ { + lo, hi := 0, n-1 + period := int64(hi) - int64(lo) + 1 + r, err := NewFC32(lo, hi, true) + if err != nil { + t.Fatal(err) + } + if r.Cycle()-2*period > period { + t.Fatalf("Cycle exceeds 3 * period") + } + } +} + +func Test1(t *testing.T) { + const ( + N = 360 + S = 3 + ) + for hq := 0; hq <= 1; hq++ { + for n := 1; n < N; n++ { + for seed := 0; seed < S; seed++ { + lo, hi := -n, 2*n + period := int64(hi - lo + 1) + r, err := NewFC32(lo, hi, hq == 1) + if err != nil { + t.Fatal(err) + } + r.Seed(int64(seed)) + m := map[int]bool{} + v := make([]int, period, period) + p := make([]int64, period, period) + for i := lo; i <= hi; i++ { + x := r.Next() + p[i-lo] = r.Pos() + if x < lo || x > hi { + t.Fatal("t1.0") + } + if m[x] { + t.Fatal("t1.1") + } + m[x] = true + v[i-lo] = x + } + for i := lo; i <= hi; i++ { + x := r.Next() + if x < lo || x > hi { + t.Fatal("t1.2") + } + if !m[x] { + t.Fatal("t1.3") + } + if x != v[i-lo] { + t.Fatal("t1.4") + } + if r.Pos() != p[i-lo] { + t.Fatal("t1.5") + } + m[x] = false + } + for i := lo; i <= hi; i++ { + r.Seek(p[i-lo] + 1) + x := r.Prev() + if x < lo || x > hi { + t.Fatal("t1.6") + } + if x != v[i-lo] { + t.Fatal("t1.7") + } + } + } + } + } +} + +func Test2(t *testing.T) { + const ( + N = 370 + S = 3 + ) + for hq := 0; hq <= 1; hq++ { + for n := 1; n < N; n++ { + for seed := 0; seed < S; seed++ { + lo, hi := -n, 2*n + period := int64(hi - lo + 1) + r, err := NewFC32(lo, hi, hq == 1) + if err != nil { + t.Fatal(err) + } + r.Seed(int64(seed)) + m := map[int]bool{} + v := make([]int, period, period) + p := make([]int64, period, period) + for i := lo; i <= hi; i++ { + x := r.Prev() + p[i-lo] = r.Pos() + if x < lo || x > hi { + t.Fatal("t2.0") + } + if m[x] { + t.Fatal("t2.1") + } + m[x] = true + v[i-lo] = x + } + for i := lo; i <= hi; i++ { + x := r.Prev() + if x < lo || x > hi { + t.Fatal("t2.2") + } + if !m[x] { + t.Fatal("t2.3") + } + if x != v[i-lo] { + t.Fatal("t2.4") + } + if r.Pos() != p[i-lo] { + t.Fatal("t2.5") + } + m[x] = false + } + for i := lo; i <= hi; i++ { + s := p[i-lo] - 1 + if s < 0 { + s = r.Cycle() - 1 + } + r.Seek(s) + x := r.Next() + if x < lo || x > hi { + t.Fatal("t2.6") + } + if x != v[i-lo] { + t.Fatal("t2.7") + } + } + } + } + } +} + +func benchmarkBig1eN(b *testing.B, r *FCBig) { + b.StartTimer() + for i := 0; i < b.N; i++ { + r.Next() + } +} + +func BenchmarkFCBig1e3(b *testing.B) { + b.StopTimer() + hi := big.NewInt(0).SetInt64(1e3) + r, _ := NewFCBig(big0, hi, false) + benchmarkBig1eN(b, r) +} + +func BenchmarkFCBig1e6(b *testing.B) { + b.StopTimer() + hi := big.NewInt(0).SetInt64(1e6) + r, _ := NewFCBig(big0, hi, false) + benchmarkBig1eN(b, r) +} + +func BenchmarkFCBig1e9(b *testing.B) { + b.StopTimer() + hi := big.NewInt(0).SetInt64(1e9) + r, _ := NewFCBig(big0, hi, false) + benchmarkBig1eN(b, r) +} + +func BenchmarkFCBig1e12(b *testing.B) { + b.StopTimer() + hi := big.NewInt(0).SetInt64(1e12) + r, _ := NewFCBig(big0, hi, false) + benchmarkBig1eN(b, r) +} + +func BenchmarkFCBig1e15(b *testing.B) { + b.StopTimer() + hi := big.NewInt(0).SetInt64(1e15) + r, _ := NewFCBig(big0, hi, false) + benchmarkBig1eN(b, r) +} + +func BenchmarkFCBig1e18(b *testing.B) { + b.StopTimer() + hi := big.NewInt(0).SetInt64(1e18) + r, _ := NewFCBig(big0, hi, false) + benchmarkBig1eN(b, r) +} + +var ( + big0 = big.NewInt(0) +) + +func TestBig0(t *testing.T) { + const N = 7400 + lo := big.NewInt(0) + hi := big.NewInt(0) + period := big.NewInt(0) + c := big.NewInt(0) + for n := int64(1); n < N; n++ { + hi.SetInt64(n - 1) + period.Set(hi) + period.Sub(period, lo) + period.Add(period, _1) + r, err := NewFCBig(lo, hi, false) + if err != nil { + t.Fatal(err) + } + if r.cycle.Cmp(period) < 0 { + t.Fatalf("Period exceeds cycle") + } + c.Set(r.Cycle()) + c.Sub(c, period) + if c.Cmp(period) > 0 { + t.Fatalf("Cycle exceeds 2 * period") + } + } + for n := int64(1); n < N; n++ { + hi.SetInt64(n - 1) + period.Set(hi) + period.Sub(period, lo) + period.Add(period, _1) + r, err := NewFCBig(lo, hi, true) + if err != nil { + t.Fatal(err) + } + if r.cycle.Cmp(period) < 0 { + t.Fatalf("Period exceeds cycle") + } + c.Set(r.Cycle()) + c.Sub(c, period) + c.Sub(c, period) + if c.Cmp(period) > 0 { + t.Fatalf("Cycle exceeds 3 * period") + } + } +} + +func TestBig1(t *testing.T) { + const ( + N = 120 + S = 3 + ) + lo := big.NewInt(0) + hi := big.NewInt(0) + seek := big.NewInt(0) + for hq := 0; hq <= 1; hq++ { + for n := int64(1); n < N; n++ { + for seed := 0; seed < S; seed++ { + lo64 := -n + hi64 := 2 * n + lo.SetInt64(lo64) + hi.SetInt64(hi64) + period := hi64 - lo64 + 1 + r, err := NewFCBig(lo, hi, hq == 1) + if err != nil { + t.Fatal(err) + } + r.Seed(int64(seed)) + m := map[int64]bool{} + v := make([]int64, period, period) + p := make([]int64, period, period) + for i := lo64; i <= hi64; i++ { + x := r.Next().Int64() + p[i-lo64] = r.Pos().Int64() + if x < lo64 || x > hi64 { + t.Fatal("tb1.0") + } + if m[x] { + t.Fatal("tb1.1") + } + m[x] = true + v[i-lo64] = x + } + for i := lo64; i <= hi64; i++ { + x := r.Next().Int64() + if x < lo64 || x > hi64 { + t.Fatal("tb1.2") + } + if !m[x] { + t.Fatal("tb1.3") + } + if x != v[i-lo64] { + t.Fatal("tb1.4") + } + if r.Pos().Int64() != p[i-lo64] { + t.Fatal("tb1.5") + } + m[x] = false + } + for i := lo64; i <= hi64; i++ { + r.Seek(seek.SetInt64(p[i-lo64] + 1)) + x := r.Prev().Int64() + if x < lo64 || x > hi64 { + t.Fatal("tb1.6") + } + if x != v[i-lo64] { + t.Fatal("tb1.7") + } + } + } + } + } +} + +func TestBig2(t *testing.T) { + const ( + N = 120 + S = 3 + ) + lo := big.NewInt(0) + hi := big.NewInt(0) + seek := big.NewInt(0) + for hq := 0; hq <= 1; hq++ { + for n := int64(1); n < N; n++ { + for seed := 0; seed < S; seed++ { + lo64, hi64 := -n, 2*n + lo.SetInt64(lo64) + hi.SetInt64(hi64) + period := hi64 - lo64 + 1 + r, err := NewFCBig(lo, hi, hq == 1) + if err != nil { + t.Fatal(err) + } + r.Seed(int64(seed)) + m := map[int64]bool{} + v := make([]int64, period, period) + p := make([]int64, period, period) + for i := lo64; i <= hi64; i++ { + x := r.Prev().Int64() + p[i-lo64] = r.Pos().Int64() + if x < lo64 || x > hi64 { + t.Fatal("tb2.0") + } + if m[x] { + t.Fatal("tb2.1") + } + m[x] = true + v[i-lo64] = x + } + for i := lo64; i <= hi64; i++ { + x := r.Prev().Int64() + if x < lo64 || x > hi64 { + t.Fatal("tb2.2") + } + if !m[x] { + t.Fatal("tb2.3") + } + if x != v[i-lo64] { + t.Fatal("tb2.4") + } + if r.Pos().Int64() != p[i-lo64] { + t.Fatal("tb2.5") + } + m[x] = false + } + for i := lo64; i <= hi64; i++ { + s := p[i-lo64] - 1 + if s < 0 { + s = r.Cycle().Int64() - 1 + } + r.Seek(seek.SetInt64(s)) + x := r.Next().Int64() + if x < lo64 || x > hi64 { + t.Fatal("tb2.6") + } + if x != v[i-lo64] { + t.Fatal("tb2.7") + } + } + } + } + } +} + +func TestPermutations(t *testing.T) { + data := sort.IntSlice{3, 2, 1} + check := [][]int{ + {1, 2, 3}, + {1, 3, 2}, + {2, 1, 3}, + {2, 3, 1}, + {3, 1, 2}, + {3, 2, 1}, + } + i := 0 + for PermutationFirst(data); ; i++ { + if i >= len(check) { + t.Fatalf("too much permutations generated: %d > %d", i+1, len(check)) + } + + for j, v := range check[i] { + got := data[j] + if got != v { + t.Fatalf("permutation %d:\ndata: %v\ncheck: %v\nexpected data[%d] == %d, got %d", i, data, check[i], j, v, got) + } + } + + if !PermutationNext(data) { + if i != len(check)-1 { + t.Fatal("permutations generated", i, "expected", len(check)) + } + break + } + } +} + +func TestIsPrime(t *testing.T) { + const p4M = 283146 // # of primes < 4e6 + n := 0 + for i := uint32(0); i <= 4e6; i++ { + if IsPrime(i) { + n++ + } + } + t.Log(n) + if n != p4M { + t.Fatal(n) + } +} + +func BenchmarkIsPrime(b *testing.B) { + b.StopTimer() + n := make([]uint32, b.N) + rng := rand.New(rand.NewSource(1)) + for i := 0; i < b.N; i++ { + n[i] = rng.Uint32() + } + b.StartTimer() + for _, n := range n { + IsPrime(n) + } +} + +func BenchmarkNextPrime(b *testing.B) { + b.StopTimer() + n := make([]uint32, b.N) + rng := rand.New(rand.NewSource(1)) + for i := 0; i < b.N; i++ { + n[i] = rng.Uint32() + } + b.StartTimer() + for _, n := range n { + NextPrime(n) + } +} + +func BenchmarkIsPrimeUint64(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + a := make([]uint64, N) + r := r64() + for i := range a { + a[i] = uint64(r.Next().Int64()) + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + IsPrimeUint64(a[i&(N-1)]) + } +} + +func BenchmarkNextPrimeUint64(b *testing.B) { + b.StopTimer() + n := make([]uint64, b.N) + rng := rand.New(rand.NewSource(1)) + for i := 0; i < b.N; i++ { + n[i] = uint64(rng.Int63()) + if i&1 == 0 { + n[i] ^= 1 << 63 + } + } + b.StartTimer() + for _, n := range n { + NextPrimeUint64(n) + } +} + +func TestNextPrime(t *testing.T) { + const p4M = 283146 // # of primes < 4e6 + n := 0 + var p uint32 + for { + p, _ = NextPrime(p) + if p >= 4e6 { + break + } + n++ + } + t.Log(n) + if n != p4M { + t.Fatal(n) + } +} + +func TestNextPrime2(t *testing.T) { + type data struct { + x uint32 + y uint32 + ok bool + } + tests := []data{ + {0, 2, true}, + {1, 2, true}, + {2, 3, true}, + {3, 5, true}, + {math.MaxUint32, 0, false}, + {math.MaxUint32 - 1, 0, false}, + {math.MaxUint32 - 2, 0, false}, + {math.MaxUint32 - 3, 0, false}, + {math.MaxUint32 - 4, 0, false}, + {math.MaxUint32 - 5, math.MaxUint32 - 4, true}, + } + + for _, test := range tests { + y, ok := NextPrime(test.x) + if ok != test.ok || ok && y != test.y { + t.Fatalf("x %d, got y %d ok %t, expected y %d ok %t", test.x, y, ok, test.y, test.ok) + } + } +} + +func TestNextPrimeUint64(t *testing.T) { + const ( + lo = 2000000000000000000 + hi = 2000000000000100000 + k = 2346 // PrimePi(hi)-PrimePi(lo) + ) + n := 0 + p := uint64(lo) - 1 + var ok bool + for { + p0 := p + p, ok = NextPrimeUint64(p) + if !ok { + t.Fatal(p0) + } + + if p > hi { + break + } + + n++ + } + if n != k { + t.Fatal(n, k) + } +} + +func TestISqrt(t *testing.T) { + for n := int64(0); n < 5e6; n++ { + x := int64(ISqrt(uint32(n))) + if x2 := x * x; x2 > n { + t.Fatalf("got ISqrt(%d) == %d, too big", n, x) + } + if x2 := x*x + 2*x + 1; x2 < n { + t.Fatalf("got ISqrt(%d) == %d, too low", n, x) + } + } + for n := int64(math.MaxUint32); n > math.MaxUint32-5e6; n-- { + x := int64(ISqrt(uint32(n))) + if x2 := x * x; x2 > n { + t.Fatalf("got ISqrt(%d) == %d, too big", n, x) + } + if x2 := x*x + 2*x + 1; x2 < n { + t.Fatalf("got ISqrt(%d) == %d, too low", n, x) + } + } + rng := rand.New(rand.NewSource(1)) + for i := 0; i < 5e6; i++ { + n := int64(rng.Uint32()) + x := int64(ISqrt(uint32(n))) + if x2 := x * x; x2 > n { + t.Fatalf("got ISqrt(%d) == %d, too big", n, x) + } + if x2 := x*x + 2*x + 1; x2 < n { + t.Fatalf("got ISqrt(%d) == %d, too low", n, x) + } + } +} + +func TestSqrtUint64(t *testing.T) { + for n := uint64(0); n < 2e6; n++ { + x := SqrtUint64(n) + if x > math.MaxUint32 { + t.Fatalf("got Sqrt(%d) == %d, too big", n, x) + } + if x2 := x * x; x2 > n { + t.Fatalf("got Sqrt(%d) == %d, too big", n, x) + } + if x2 := x*x + 2*x + 1; x2 < n { + t.Fatalf("got Sqrt(%d) == %d, too low", n, x) + } + } + const H = uint64(18446744056529682436) + for n := H; n > H-2e6; n-- { + x := SqrtUint64(n) + if x > math.MaxUint32 { + t.Fatalf("got Sqrt(%d) == %d, too big", n, x) + } + if x2 := x * x; x2 > n { + t.Fatalf("got Sqrt(%d) == %d, too big", n, x) + } + if x2 := x*x + 2*x + 1; x2 < n { + t.Fatalf("got Sqrt(%d) == %d, too low", n, x) + } + } + rng := rand.New(rand.NewSource(1)) + for i := 0; i < 2e6; i++ { + n := uint64(rng.Uint32())<<31 | uint64(rng.Uint32()) + x := SqrtUint64(n) + if x2 := x * x; x2 > n { + t.Fatalf("got Sqrt(%d) == %d, too big", n, x) + } + if x2 := x*x + 2*x + 1; x2 < n { + t.Fatalf("got Sqrt(%d) == %d, too low", n, x) + } + } +} + +func TestSqrtBig(t *testing.T) { + const N = 3e4 + var n, lim, x2 big.Int + lim.SetInt64(N) + for n.Cmp(&lim) != 0 { + x := SqrtBig(&n) + x2.Mul(x, x) + if x.Cmp(&n) > 0 { + t.Fatalf("got sqrt(%s) == %s, too big", &n, x) + } + x2.Add(&x2, x) + x2.Add(&x2, x) + x2.Add(&x2, _1) + if x2.Cmp(&n) < 0 { + t.Fatalf("got sqrt(%s) == %s, too low", &n, x) + } + n.Add(&n, _1) + } + rng := rand.New(rand.NewSource(1)) + var h big.Int + h.SetBit(&h, 1e3, 1) + for i := 0; i < N; i++ { + n.Rand(rng, &h) + x := SqrtBig(&n) + x2.Mul(x, x) + if x.Cmp(&n) > 0 { + t.Fatalf("got sqrt(%s) == %s, too big", &n, x) + } + x2.Add(&x2, x) + x2.Add(&x2, x) + x2.Add(&x2, _1) + if x2.Cmp(&n) < 0 { + t.Fatalf("got sqrt(%s) == %s, too low", &n, x) + } + } +} + +func TestFactorInt(t *testing.T) { + chk := func(n uint64, f []FactorTerm) bool { + if n < 2 { + return len(f) == 0 + } + + for i := 1; i < len(f); i++ { // verify ordering + if t, u := f[i-1], f[i]; t.Prime >= u.Prime { + return false + } + } + + x := uint64(1) + for _, v := range f { + if p := v.Prime; p < 0 || !IsPrime(uint32(v.Prime)) { + return false + } + + for i := uint32(0); i < v.Power; i++ { + x *= uint64(v.Prime) + if x > math.MaxUint32 { + return false + } + } + } + return x == n + } + + for n := uint64(0); n < 3e5; n++ { + f := FactorInt(uint32(n)) + if !chk(n, f) { + t.Fatalf("bad FactorInt(%d): %v", n, f) + } + } + for n := uint64(math.MaxUint32); n > math.MaxUint32-12e4; n-- { + f := FactorInt(uint32(n)) + if !chk(n, f) { + t.Fatalf("bad FactorInt(%d): %v", n, f) + } + } + rng := rand.New(rand.NewSource(1)) + for i := 0; i < 13e4; i++ { + n := rng.Uint32() + f := FactorInt(n) + if !chk(uint64(n), f) { + t.Fatalf("bad FactorInt(%d): %v", n, f) + } + } +} + +func TestFactorIntB(t *testing.T) { + const N = 3e5 // must be < math.MaxInt32 + factors := make([][]FactorTerm, N+1) + // set up the divisors + for prime := uint32(2); prime <= N; prime, _ = NextPrime(prime) { + for n := int(prime); n <= N; n += int(prime) { + factors[n] = append(factors[n], FactorTerm{prime, 0}) + } + } + // set up the powers + for n := 2; n <= N; n++ { + f := factors[n] + m := uint32(n) + for i, v := range f { + for m%v.Prime == 0 { + m /= v.Prime + v.Power++ + } + f[i] = v + } + factors[n] = f + } + // check equal + for n, e := range factors { + g := FactorInt(uint32(n)) + if len(e) != len(g) { + t.Fatal(n, "len", g, "!=", e) + } + + for i, ev := range e { + gv := g[i] + if ev.Prime != gv.Prime { + t.Fatal(n, "prime", gv, ev) + } + + if ev.Power != gv.Power { + t.Fatal(n, "power", gv, ev) + } + } + } +} + +func BenchmarkISqrt(b *testing.B) { + b.StopTimer() + n := make([]uint32, b.N) + rng := rand.New(rand.NewSource(1)) + for i := 0; i < b.N; i++ { + n[i] = rng.Uint32() + } + b.StartTimer() + for _, n := range n { + ISqrt(n) + } +} + +func BenchmarkSqrtUint64(b *testing.B) { + b.StopTimer() + n := make([]uint64, b.N) + rng := rand.New(rand.NewSource(1)) + for i := 0; i < b.N; i++ { + n[i] = uint64(rng.Uint32())<<32 | uint64(rng.Uint32()) + } + b.StartTimer() + for _, n := range n { + SqrtUint64(n) + } +} + +func benchmarkSqrtBig(b *testing.B, bits int) { + b.StopTimer() + n := make([]*big.Int, b.N) + rng := rand.New(rand.NewSource(1)) + var nn, h big.Int + h.SetBit(&h, bits, 1) + for i := 0; i < b.N; i++ { + n[i] = nn.Rand(rng, &h) + } + runtime.GC() + b.StartTimer() + for _, n := range n { + SqrtBig(n) + } +} + +func BenchmarkSqrtBig2e1e1(b *testing.B) { + benchmarkSqrtBig(b, 1e1) +} + +func BenchmarkSqrtBig2e1e2(b *testing.B) { + benchmarkSqrtBig(b, 1e2) +} + +func BenchmarkSqrtBig2e1e3(b *testing.B) { + benchmarkSqrtBig(b, 1e3) +} + +func BenchmarkSqrtBig2e1e4(b *testing.B) { + benchmarkSqrtBig(b, 1e4) +} + +func BenchmarkSqrtBig2e1e5(b *testing.B) { + benchmarkSqrtBig(b, 1e5) +} + +func BenchmarkFactorInt(b *testing.B) { + b.StopTimer() + n := make([]uint32, b.N) + rng := rand.New(rand.NewSource(1)) + for i := 0; i < b.N; i++ { + n[i] = rng.Uint32() + } + b.StartTimer() + for _, n := range n { + FactorInt(n) + } +} + +func TestIsPrimeUint16(t *testing.T) { + for n := 0; n <= math.MaxUint16; n++ { + if IsPrimeUint16(uint16(n)) != IsPrime(uint32(n)) { + t.Fatal(n) + } + } +} + +func BenchmarkIsPrimeUint16(b *testing.B) { + b.StopTimer() + n := make([]uint16, b.N) + rng := rand.New(rand.NewSource(1)) + for i := 0; i < b.N; i++ { + n[i] = uint16(rng.Uint32()) + } + b.StartTimer() + for _, n := range n { + IsPrimeUint16(n) + } +} + +func TestNextPrimeUint16(t *testing.T) { + for n := 0; n <= math.MaxUint16; n++ { + p, ok := NextPrimeUint16(uint16(n)) + p2, ok2 := NextPrime(uint32(n)) + switch { + case ok: + if !ok2 || uint32(p) != p2 { + t.Fatal(n, p, ok) + } + case !ok && ok2: + if p2 < 65536 { + t.Fatal(n, p, ok) + } + } + } +} + +func BenchmarkNextPrimeUint16(b *testing.B) { + b.StopTimer() + n := make([]uint16, b.N) + rng := rand.New(rand.NewSource(1)) + for i := 0; i < b.N; i++ { + n[i] = uint16(rng.Uint32()) + } + b.StartTimer() + for _, n := range n { + NextPrimeUint16(n) + } +} + +/* + +From: http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan + +Counting bits set, Brian Kernighan's way + +unsigned int v; // count the number of bits set in v +unsigned int c; // c accumulates the total bits set in v +for (c = 0; v; c++) +{ + v &= v - 1; // clear the least significant bit set +} + +Brian Kernighan's method goes through as many iterations as there are set bits. +So if we have a 32-bit word with only the high bit set, then it will only go +once through the loop. + +Published in 1988, the C Programming Language 2nd Ed. (by Brian W. Kernighan +and Dennis M. Ritchie) mentions this in exercise 2-9. On April 19, 2006 Don +Knuth pointed out to me that this method "was first published by Peter Wegner +in CACM 3 (1960), 322. (Also discovered independently by Derrick Lehmer and +published in 1964 in a book edited by Beckenbach.)" +*/ +func bcnt(v uint64) (c int) { + for ; v != 0; c++ { + v &= v - 1 + } + return +} + +func TestPopCount(t *testing.T) { + const N = 4e5 + maxUint64 := big.NewInt(0) + maxUint64.SetBit(maxUint64, 64, 1) + maxUint64.Sub(maxUint64, big.NewInt(1)) + rng := r64() + for i := 0; i < N; i++ { + n := uint64(rng.Next().Int64()) + if g, e := PopCountByte(byte(n)), bcnt(uint64(byte(n))); g != e { + t.Fatal(n, g, e) + } + + if g, e := PopCountUint16(uint16(n)), bcnt(uint64(uint16(n))); g != e { + t.Fatal(n, g, e) + } + + if g, e := PopCountUint32(uint32(n)), bcnt(uint64(uint32(n))); g != e { + t.Fatal(n, g, e) + } + + if g, e := PopCount(int(n)), bcnt(uint64(uint(n))); g != e { + t.Fatal(n, g, e) + } + + if g, e := PopCountUint(uint(n)), bcnt(uint64(uint(n))); g != e { + t.Fatal(n, g, e) + } + + if g, e := PopCountUint64(n), bcnt(n); g != e { + t.Fatal(n, g, e) + } + + if g, e := PopCountUintptr(uintptr(n)), bcnt(uint64(uintptr(n))); g != e { + t.Fatal(n, g, e) + } + } +} + +var gcds = []struct{ a, b, gcd uint64 }{ + {8, 12, 4}, + {12, 18, 6}, + {42, 56, 14}, + {54, 24, 6}, + {252, 105, 21}, + {1989, 867, 51}, + {1071, 462, 21}, + {2 * 3 * 5 * 7 * 11, 5 * 7 * 11 * 13 * 17, 5 * 7 * 11}, + {2 * 3 * 5 * 7 * 7 * 11, 5 * 7 * 7 * 11 * 13 * 17, 5 * 7 * 7 * 11}, + {2 * 3 * 5 * 7 * 7 * 11, 5 * 7 * 7 * 13 * 17, 5 * 7 * 7}, + {2 * 3 * 5 * 7 * 11, 13 * 17 * 19, 1}, +} + +func TestGCD(t *testing.T) { + for i, v := range gcds { + if v.a <= math.MaxUint16 && v.b <= math.MaxUint16 { + if g, e := uint64(GCDUint16(uint16(v.a), uint16(v.b))), v.gcd; g != e { + t.Errorf("%d: got gcd(%d, %d) %d, exp %d", i, v.a, v.b, g, e) + } + if g, e := uint64(GCDUint16(uint16(v.b), uint16(v.a))), v.gcd; g != e { + t.Errorf("%d: got gcd(%d, %d) %d, exp %d", i, v.b, v.a, g, e) + } + } + if v.a <= math.MaxUint32 && v.b <= math.MaxUint32 { + if g, e := uint64(GCDUint32(uint32(v.a), uint32(v.b))), v.gcd; g != e { + t.Errorf("%d: got gcd(%d, %d) %d, exp %d", i, v.a, v.b, g, e) + } + if g, e := uint64(GCDUint32(uint32(v.b), uint32(v.a))), v.gcd; g != e { + t.Errorf("%d: got gcd(%d, %d) %d, exp %d", i, v.b, v.a, g, e) + } + } + if g, e := GCDUint64(v.a, v.b), v.gcd; g != e { + t.Errorf("%d: got gcd(%d, %d) %d, exp %d", i, v.a, v.b, g, e) + } + if g, e := GCDUint64(v.b, v.a), v.gcd; g != e { + t.Errorf("%d: got gcd(%d, %d) %d, exp %d", i, v.b, v.a, g, e) + } + } +} + +func lg2(n uint64) (lg int) { + if n == 0 { + return -1 + } + + for n >>= 1; n != 0; n >>= 1 { + lg++ + } + return +} + +func TestLog2(t *testing.T) { + if g, e := Log2Byte(0), -1; g != e { + t.Error(g, e) + } + if g, e := Log2Uint16(0), -1; g != e { + t.Error(g, e) + } + if g, e := Log2Uint32(0), -1; g != e { + t.Error(g, e) + } + if g, e := Log2Uint64(0), -1; g != e { + t.Error(g, e) + } + const N = 1e6 + rng := r64() + for i := 0; i < N; i++ { + n := uint64(rng.Next().Int64()) + if g, e := Log2Uint64(n), lg2(n); g != e { + t.Fatalf("%b %d %d", n, g, e) + } + if g, e := Log2Uint32(uint32(n)), lg2(n&0xffffffff); g != e { + t.Fatalf("%b %d %d", n, g, e) + } + if g, e := Log2Uint16(uint16(n)), lg2(n&0xffff); g != e { + t.Fatalf("%b %d %d", n, g, e) + } + if g, e := Log2Byte(byte(n)), lg2(n&0xff); g != e { + t.Fatalf("%b %d %d", n, g, e) + } + } +} + +func TestBitLen(t *testing.T) { + if g, e := BitLenByte(0), 0; g != e { + t.Error(g, e) + } + if g, e := BitLenUint16(0), 0; g != e { + t.Error(g, e) + } + if g, e := BitLenUint32(0), 0; g != e { + t.Error(g, e) + } + if g, e := BitLenUint64(0), 0; g != e { + t.Error(g, e) + } + if g, e := BitLenUintptr(0), 0; g != e { + t.Error(g, e) + } + const N = 1e6 + rng := r64() + for i := 0; i < N; i++ { + n := uint64(rng.Next().Int64()) + if g, e := BitLenUintptr(uintptr(n)), lg2(uint64(uintptr(n)))+1; g != e { + t.Fatalf("%b %d %d", n, g, e) + } + if g, e := BitLenUint64(n), lg2(n)+1; g != e { + t.Fatalf("%b %d %d", n, g, e) + } + if g, e := BitLenUint32(uint32(n)), lg2(n&0xffffffff)+1; g != e { + t.Fatalf("%b %d %d", n, g, e) + } + if g, e := BitLen(int(n)), lg2(uint64(uint(n)))+1; g != e { + t.Fatalf("%b %d %d", n, g, e) + } + if g, e := BitLenUint(uint(n)), lg2(uint64(uint(n)))+1; g != e { + t.Fatalf("%b %d %d", n, g, e) + } + if g, e := BitLenUint16(uint16(n)), lg2(n&0xffff)+1; g != e { + t.Fatalf("%b %d %d", n, g, e) + } + if g, e := BitLenByte(byte(n)), lg2(n&0xff)+1; g != e { + t.Fatalf("%b %d %d", n, g, e) + } + } +} + +func BenchmarkGCDByte(b *testing.B) { + const N = 1 << 16 + type t byte + type u struct{ a, b t } + b.StopTimer() + rng := r32() + a := make([]u, N) + for i := range a { + a[i] = u{t(rng.Next()), t(rng.Next())} + } + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + GCDByte(byte(v.a), byte(v.b)) + } +} + +func BenchmarkGCDUint16(b *testing.B) { + const N = 1 << 16 + type t uint16 + type u struct{ a, b t } + b.StopTimer() + rng := r32() + a := make([]u, N) + for i := range a { + a[i] = u{t(rng.Next()), t(rng.Next())} + } + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + GCDUint16(uint16(v.a), uint16(v.b)) + } +} + +func BenchmarkGCDUint32(b *testing.B) { + const N = 1 << 16 + type t uint32 + type u struct{ a, b t } + b.StopTimer() + rng := r32() + a := make([]u, N) + for i := range a { + a[i] = u{t(rng.Next()), t(rng.Next())} + } + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + GCDUint32(uint32(v.a), uint32(v.b)) + } +} + +func BenchmarkGCDUint64(b *testing.B) { + const N = 1 << 16 + type t uint64 + type u struct{ a, b t } + b.StopTimer() + rng := r64() + a := make([]u, N) + for i := range a { + a[i] = u{t(rng.Next().Int64()), t(rng.Next().Int64())} + } + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + GCDUint64(uint64(v.a), uint64(v.b)) + } +} + +func BenchmarkLog2Byte(b *testing.B) { + const N = 1 << 16 + type t byte + b.StopTimer() + rng := r32() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + Log2Byte(byte(a[i&(N-1)])) + } +} + +func BenchmarkLog2Uint16(b *testing.B) { + const N = 1 << 16 + type t uint16 + b.StopTimer() + rng := r32() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + Log2Uint16(uint16(a[i&(N-1)])) + } +} + +func BenchmarkLog2Uint32(b *testing.B) { + const N = 1 << 16 + type t uint32 + b.StopTimer() + rng := r32() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + Log2Uint32(uint32(a[i&(N-1)])) + } +} + +func BenchmarkLog2Uint64(b *testing.B) { + const N = 1 << 16 + type t uint64 + b.StopTimer() + rng := r64() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next().Int64()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + Log2Uint64(uint64(a[i&(N-1)])) + } +} +func BenchmarkBitLenByte(b *testing.B) { + const N = 1 << 16 + type t byte + b.StopTimer() + rng := r32() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + BitLenByte(byte(a[i&(N-1)])) + } +} + +func BenchmarkBitLenUint16(b *testing.B) { + const N = 1 << 16 + type t uint16 + b.StopTimer() + rng := r32() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + BitLenUint16(uint16(a[i&(N-1)])) + } +} + +func BenchmarkBitLenUint32(b *testing.B) { + const N = 1 << 16 + type t uint32 + b.StopTimer() + rng := r32() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + BitLenUint32(uint32(a[i&(N-1)])) + } +} + +func BenchmarkBitLen(b *testing.B) { + const N = 1 << 16 + type t int + b.StopTimer() + rng := r64() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next().Int64()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + BitLen(int(a[i&(N-1)])) + } +} + +func BenchmarkBitLenUint(b *testing.B) { + const N = 1 << 16 + type t uint + b.StopTimer() + rng := r64() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next().Int64()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + BitLenUint(uint(a[i&(N-1)])) + } +} + +func BenchmarkBitLenUintptr(b *testing.B) { + const N = 1 << 16 + type t uintptr + b.StopTimer() + rng := r64() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next().Int64()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + BitLenUintptr(uintptr(a[i&(N-1)])) + } +} + +func BenchmarkBitLenUint64(b *testing.B) { + const N = 1 << 16 + type t uint64 + b.StopTimer() + rng := r64() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next().Int64()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + BitLenUint64(uint64(a[i&(N-1)])) + } +} + +func BenchmarkPopCountByte(b *testing.B) { + const N = 1 << 16 + type t byte + b.StopTimer() + rng := r32() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + PopCountByte(byte(a[i&(N-1)])) + } +} + +func BenchmarkPopCountUint16(b *testing.B) { + const N = 1 << 16 + type t uint16 + b.StopTimer() + rng := r32() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + PopCountUint16(uint16(a[i&(N-1)])) + } +} + +func BenchmarkPopCountUint32(b *testing.B) { + const N = 1 << 16 + type t uint32 + b.StopTimer() + rng := r32() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + PopCountUint32(uint32(a[i&(N-1)])) + } +} + +func BenchmarkPopCount(b *testing.B) { + const N = 1 << 16 + type t int + b.StopTimer() + rng := r64() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next().Int64()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + PopCount(int(a[i&(N-1)])) + } +} + +func BenchmarkPopCountUint(b *testing.B) { + const N = 1 << 16 + type t uint + b.StopTimer() + rng := r64() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next().Int64()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + PopCountUint(uint(a[i&(N-1)])) + } +} + +func BenchmarkPopCountUintptr(b *testing.B) { + const N = 1 << 16 + type t uintptr + b.StopTimer() + rng := r64() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next().Int64()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + PopCountUintptr(uintptr(a[i&(N-1)])) + } +} + +func BenchmarkPopCountUint64(b *testing.B) { + const N = 1 << 16 + type t uint64 + b.StopTimer() + rng := r64() + a := make([]t, N) + for i := range a { + a[i] = t(rng.Next().Int64()) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + PopCountUint64(uint64(a[i&(N-1)])) + } +} + +func TestUintptrBits(t *testing.T) { + switch g := UintptrBits(); g { + case 32, 64: + // ok + t.Log(g) + default: + t.Fatalf("got %d, expected 32 or 64", g) + } +} + +func BenchmarkUintptrBits(b *testing.B) { + for i := 0; i < b.N; i++ { + UintptrBits() + } +} + +func TestModPowByte(t *testing.T) { + data := []struct{ b, e, m, r byte }{ + {0, 1, 1, 0}, + {0, 2, 1, 0}, + {0, 3, 1, 0}, + + {1, 0, 1, 0}, + {1, 1, 1, 0}, + {1, 2, 1, 0}, + {1, 3, 1, 0}, + + {2, 0, 1, 0}, + {2, 1, 1, 0}, + {2, 2, 1, 0}, + {2, 3, 1, 0}, + + {2, 11, 23, 1}, // 23|M11 + {2, 11, 89, 1}, // 89|M11 + {2, 23, 47, 1}, // 47|M23 + {5, 3, 13, 8}, + } + + for _, v := range data { + if g, e := ModPowByte(v.b, v.e, v.m), v.r; g != e { + t.Errorf("b %d e %d m %d: got %d, exp %d", v.b, v.e, v.m, g, e) + } + } +} + +func TestModPowUint16(t *testing.T) { + data := []struct{ b, e, m, r uint16 }{ + {0, 1, 1, 0}, + {0, 2, 1, 0}, + {0, 3, 1, 0}, + + {1, 0, 1, 0}, + {1, 1, 1, 0}, + {1, 2, 1, 0}, + {1, 3, 1, 0}, + + {2, 0, 1, 0}, + {2, 1, 1, 0}, + {2, 2, 1, 0}, + {2, 3, 1, 0}, + + {2, 11, 23, 1}, // 23|M11 + {2, 11, 89, 1}, // 89|M11 + {2, 23, 47, 1}, // 47|M23 + {2, 929, 13007, 1}, // 13007|M929 + {4, 13, 497, 445}, + {5, 3, 13, 8}, + } + + for _, v := range data { + if g, e := ModPowUint16(v.b, v.e, v.m), v.r; g != e { + t.Errorf("b %d e %d m %d: got %d, exp %d", v.b, v.e, v.m, g, e) + } + } +} + +func TestModPowUint32(t *testing.T) { + data := []struct{ b, e, m, r uint32 }{ + {0, 1, 1, 0}, + {0, 2, 1, 0}, + {0, 3, 1, 0}, + + {1, 0, 1, 0}, + {1, 1, 1, 0}, + {1, 2, 1, 0}, + {1, 3, 1, 0}, + + {2, 0, 1, 0}, + {2, 1, 1, 0}, + {2, 2, 1, 0}, + {2, 3, 1, 0}, + + {2, 23, 47, 1}, // 47|M23 + {2, 67, 193707721, 1}, // 193707721|M67 + {2, 929, 13007, 1}, // 13007|M929 + {4, 13, 497, 445}, + {5, 3, 13, 8}, + {2, 500471, 264248689, 1}, + {2, 1000249, 112027889, 1}, + {2, 2000633, 252079759, 1}, + {2, 3000743, 222054983, 1}, + {2, 4000741, 1920355681, 1}, + {2, 5000551, 330036367, 1}, + {2, 6000479, 1020081431, 1}, + {2, 7000619, 840074281, 1}, + {2, 8000401, 624031279, 1}, + {2, 9000743, 378031207, 1}, + {2, 10000961, 380036519, 1}, + {2, 20000723, 40001447, 1}, + } + + for _, v := range data { + if g, e := ModPowUint32(v.b, v.e, v.m), v.r; g != e { + t.Errorf("b %d e %d m %d: got %d, exp %d", v.b, v.e, v.m, g, e) + } + } +} + +func TestModPowUint64(t *testing.T) { + data := []struct{ b, e, m, r uint64 }{ + {0, 1, 1, 0}, + {0, 2, 1, 0}, + {0, 3, 1, 0}, + + {1, 0, 1, 0}, + {1, 1, 1, 0}, + {1, 2, 1, 0}, + {1, 3, 1, 0}, + + {2, 0, 1, 0}, + {2, 1, 1, 0}, + {2, 2, 1, 0}, + {2, 3, 1, 0}, + + {2, 23, 47, 1}, // 47|M23 + {2, 67, 193707721, 1}, // 193707721|M67 + {2, 929, 13007, 1}, // 13007|M929 + {4, 13, 497, 445}, + {5, 3, 13, 8}, + {2, 500471, 264248689, 1}, // m|Me ... + {2, 1000249, 112027889, 1}, + {2, 2000633, 252079759, 1}, + {2, 3000743, 222054983, 1}, + {2, 4000741, 1920355681, 1}, + {2, 5000551, 330036367, 1}, + {2, 6000479, 1020081431, 1}, + {2, 7000619, 840074281, 1}, + {2, 8000401, 624031279, 1}, + {2, 9000743, 378031207, 1}, + {2, 10000961, 380036519, 1}, + {2, 20000723, 40001447, 1}, + {2, 1000099, 1872347344039, 1}, + + {9223372036854775919, 9223372036854776030, 9223372036854776141, 7865333882915297658}, + } + + for _, v := range data { + if g, e := ModPowUint64(v.b, v.e, v.m), v.r; g != e { + t.Errorf("b %d e %d m %d: got %d, exp %d", v.b, v.e, v.m, g, e) + } + } +} + +func TestModPowBigInt(t *testing.T) { + data := []struct { + b, e int64 + m interface{} + r int64 + }{ + {0, 1, 1, 0}, + {0, 2, 1, 0}, + {0, 3, 1, 0}, + + {1, 0, 1, 0}, + {1, 1, 1, 0}, + {1, 2, 1, 0}, + {1, 3, 1, 0}, + + {2, 0, 1, 0}, + {2, 1, 1, 0}, + {2, 2, 1, 0}, + {2, 3, 1, 0}, + + {2, 23, 47, 1}, // 47|M23 + {2, 67, 193707721, 1}, // 193707721|M67 + {2, 929, 13007, 1}, // 13007|M929 + {4, 13, 497, 445}, + {5, 3, 13, 8}, + {2, 500471, 264248689, 1}, // m|Me ... + {2, 1000249, 112027889, 1}, + {2, 2000633, 252079759, 1}, + {2, 3000743, 222054983, 1}, + {2, 4000741, 1920355681, 1}, + {2, 5000551, 330036367, 1}, + {2, 6000479, 1020081431, 1}, + {2, 7000619, 840074281, 1}, + {2, 8000401, 624031279, 1}, + {2, 9000743, 378031207, 1}, + {2, 10000961, 380036519, 1}, + {2, 20000723, 40001447, 1}, + {2, 100279, "11502865265922183403581252152383", 1}, + + {2, 7293457, "533975545077050000610542659519277030089249998649", 1}, + } + + for _, v := range data { + var m big.Int + switch x := v.m.(type) { + case int: + m.SetInt64(int64(x)) + case string: + m.SetString(x, 10) + } + b, e, r := big.NewInt(v.b), big.NewInt(v.e), big.NewInt(v.r) + if g, e := ModPowBigInt(b, e, &m), r; g.Cmp(e) != 0 { + t.Errorf("b %s e %s m %v: got %s, exp %s", b, e, m, g, e) + } + } + + s := func(n string) *big.Int { + i, ok := big.NewInt(0).SetString(n, 10) + if !ok { + t.Fatal(ok) + } + + return i + } + + if g, e := ModPowBigInt( + s("36893488147419103343"), s("36893488147419103454"), s("36893488147419103565")), s("34853007610367449339"); g.Cmp(e) != 0 { + t.Fatal(g, e) + } +} + +func BenchmarkModPowByte(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct{ b, e, m byte } + a := make([]t, N) + r := r32() + for i := range a { + a[i] = t{ + byte(r.Next() | 2), + byte(r.Next() | 2), + byte(r.Next() | 2), + } + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + ModPowByte(v.b, v.e, v.m) + } +} + +func BenchmarkModPowUint16(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct{ b, e, m uint16 } + a := make([]t, N) + r := r32() + for i := range a { + a[i] = t{ + uint16(r.Next() | 2), + uint16(r.Next() | 2), + uint16(r.Next() | 2), + } + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + ModPowUint16(v.b, v.e, v.m) + } +} + +func BenchmarkModPowUint32(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct{ b, e, m uint32 } + a := make([]t, N) + r := r32() + for i := range a { + a[i] = t{ + uint32(r.Next() | 2), + uint32(r.Next() | 2), + uint32(r.Next() | 2), + } + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + ModPowUint32(v.b, v.e, v.m) + } +} + +func BenchmarkModPowUint64(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct{ b, e, m uint64 } + a := make([]t, N) + r := r64() + for i := range a { + a[i] = t{ + uint64(r.Next().Int64() | 2), + uint64(r.Next().Int64() | 2), + uint64(r.Next().Int64() | 2), + } + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + ModPowUint64(v.b, v.e, v.m) + } +} + +func BenchmarkModPowBigInt(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct{ b, e, m *big.Int } + a := make([]t, N) + mx := big.NewInt(math.MaxInt64) + mx.Mul(mx, mx) + r, err := NewFCBig(big.NewInt(2), mx, true) + if err != nil { + b.Fatal(err) + } + for i := range a { + a[i] = t{ + r.Next(), + r.Next(), + r.Next(), + } + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + ModPowBigInt(v.b, v.e, v.m) + } +} + +func TestAdd128(t *testing.T) { + const N = 1e5 + r := r64() + var mm big.Int + for i := 0; i < N; i++ { + a, b := uint64(r.Next().Int64()), uint64(r.Next().Int64()) + aa, bb := big.NewInt(0).SetUint64(a), big.NewInt(0).SetUint64(b) + mhi, mlo := AddUint128_64(a, b) + m := big.NewInt(0).SetUint64(mhi) + m.Lsh(m, 64) + m.Add(m, big.NewInt(0).SetUint64(mlo)) + mm.Add(aa, bb) + if m.Cmp(&mm) != 0 { + t.Fatalf("%d\na %40d\nb %40d\ng %40s %032x\ne %40s %032x", i, a, b, m, m, &mm, &mm) + } + } +} + +func TestMul128(t *testing.T) { + const N = 1e5 + r := r64() + var mm big.Int + f := func(a, b uint64) { + aa, bb := big.NewInt(0).SetUint64(a), big.NewInt(0).SetUint64(b) + mhi, mlo := MulUint128_64(a, b) + m := big.NewInt(0).SetUint64(mhi) + m.Lsh(m, 64) + m.Add(m, big.NewInt(0).SetUint64(mlo)) + mm.Mul(aa, bb) + if m.Cmp(&mm) != 0 { + t.Fatalf("\na %40d\nb %40d\ng %40s %032x\ne %40s %032x", a, b, m, m, &mm, &mm) + } + } + for i := 0; i < N; i++ { + f(uint64(r.Next().Int64()), uint64(r.Next().Int64())) + } + for x := 0; x <= 1<<9; x++ { + for y := 0; y <= 1<<9; y++ { + f(math.MaxUint64-uint64(x), math.MaxUint64-uint64(y)) + } + } +} + +func BenchmarkMul128(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct{ a, b uint64 } + a := make([]t, N) + r := r64() + for i := range a { + a[i] = t{ + uint64(r.Next().Int64()), + uint64(r.Next().Int64()), + } + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + MulUint128_64(v.a, v.b) + } +} + +func BenchmarkMul128Big(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct{ a, b *big.Int } + a := make([]t, N) + r := r64() + for i := range a { + a[i] = t{ + big.NewInt(r.Next().Int64()), + big.NewInt(r.Next().Int64()), + } + } + var x big.Int + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + x.Mul(v.a, v.b) + } +} + +func TestIsPrimeUint64(t *testing.T) { + f := func(lo, hi uint64, exp int) { + got := 0 + for n := lo; n <= hi; { + if IsPrimeUint64(n) { + got++ + } + n0 := n + n++ + if n < n0 { + break + } + } + if got != exp { + t.Fatal(lo, hi, got, exp) + } + } + + // lo, hi, PrimePi(hi)-PrimePi(lo) + f(0, 1e4, 1229) + f(1e5, 1e5+1e4, 861) + f(1e6, 1e6+1e4, 753) + f(1e7, 1e7+1e4, 614) + f(1e8, 1e8+1e4, 551) + f(1e9, 1e9+1e4, 487) + f(1e10, 1e10+1e4, 406) + f(1e11, 1e11+1e4, 394) + f(1e12, 1e12+1e4, 335) + f(1e13, 1e13+1e4, 354) + f(1e14, 1e14+1e4, 304) + f(1e15, 1e15+1e4, 263) + f(1e16, 1e16+1e4, 270) + f(1e17, 1e17+1e4, 265) + f(1e18, 1e18+1e4, 241) + f(1e19, 1e19+1e4, 255) + f(1<<64-1e4, 1<<64-1, 218) +} + +func TestProbablyPrimeUint32(t *testing.T) { + f := func(n, firstFail uint32, primes []uint32) { + for ; n <= firstFail; n += 2 { + prp := true + for _, a := range primes { + if !ProbablyPrimeUint32(n, a) { + prp = false + break + } + } + if prp != IsPrime(n) && n != firstFail { + t.Fatal(n) + } + } + } + if !ProbablyPrimeUint32(5, 2) { + t.Fatal(false) + } + if !ProbablyPrimeUint32(7, 2) { + t.Fatal(false) + } + if ProbablyPrimeUint32(9, 2) { + t.Fatal(true) + } + if !ProbablyPrimeUint32(11, 2) { + t.Fatal(false) + } + // http://oeis.org/A014233 + f(5, 2047, []uint32{2}) + f(2047, 1373653, []uint32{2, 3}) + f(1373653, 25326001, []uint32{2, 3, 5}) +} + +func BenchmarkProbablyPrimeUint32(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct{ n, a uint32 } + data := make([]t, N) + r := r32() + for i := range data { + n := uint32(r.Next()) | 1 + if n <= 3 { + n += 5 + } + a := uint32(r.Next()) + if a <= 1 { + a += 2 + } + data[i] = t{n, a} + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := data[i&(N-1)] + ProbablyPrimeUint32(v.n, v.a) + } +} + +func TestProbablyPrimeUint64_32(t *testing.T) { + f := func(n, firstFail uint64, primes []uint32) { + for ; n <= firstFail; n += 2 { + prp := true + for _, a := range primes { + if !ProbablyPrimeUint64_32(n, a) { + prp = false + break + } + } + if prp != IsPrimeUint64(n) && n != firstFail { + t.Fatal(n) + } + } + } + if !ProbablyPrimeUint64_32(5, 2) { + t.Fatal(false) + } + if !ProbablyPrimeUint64_32(7, 2) { + t.Fatal(false) + } + if ProbablyPrimeUint64_32(9, 2) { + t.Fatal(true) + } + if !ProbablyPrimeUint64_32(11, 2) { + t.Fatal(false) + } + // http://oeis.org/A014233 + f(5, 2047, []uint32{2}) + f(2047, 1373653, []uint32{2, 3}) +} + +func BenchmarkProbablyPrimeUint64_32(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct { + n uint64 + a uint32 + } + data := make([]t, N) + r := r32() + r2 := r64() + for i := range data { + var n uint64 + for n <= 3 { + n = uint64(r2.Next().Int64()) | 1 + } + var a uint32 + for a <= 1 { + a = uint32(r.Next()) + } + data[i] = t{n, a} + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := data[i&(N-1)] + ProbablyPrimeUint64_32(v.n, v.a) + } +} + +func TestProbablyPrimeBigInt_32(t *testing.T) { + f := func(n0, firstFail0 uint64, primes []uint32) { + n, firstFail := big.NewInt(0).SetUint64(n0), big.NewInt(0).SetUint64(firstFail0) + for ; n.Cmp(firstFail) <= 0; n.Add(n, _2) { + prp := true + for _, a := range primes { + if !ProbablyPrimeBigInt_32(n, a) { + prp = false + break + } + } + if prp != IsPrimeUint64(n0) && n0 != firstFail0 { + t.Fatal(n) + } + n0 += 2 + } + } + if !ProbablyPrimeBigInt_32(big.NewInt(5), 2) { + t.Fatal(false) + } + if !ProbablyPrimeBigInt_32(big.NewInt(7), 2) { + t.Fatal(false) + } + if ProbablyPrimeBigInt_32(big.NewInt(9), 2) { + t.Fatal(true) + } + if !ProbablyPrimeBigInt_32(big.NewInt(11), 2) { + t.Fatal(false) + } + // http://oeis.org/A014233 + f(5, 2047, []uint32{2}) + f(2047, 1373653, []uint32{2, 3}) +} + +func BenchmarkProbablyPrimeBigInt_32(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct { + n *big.Int + a uint32 + } + data := make([]t, N) + r := r32() + r2 := r64() + for i := range data { + var n uint64 + for n <= 3 { + n = uint64(r2.Next().Int64()) | 1 + } + var a uint32 + for a <= 1 { + a = uint32(r.Next()) + } + data[i] = t{big.NewInt(0).SetUint64(n), a} + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := data[i&(N-1)] + ProbablyPrimeBigInt_32(v.n, v.a) + } +} + +func TestProbablyPrimeBigInt(t *testing.T) { + f := func(n0, firstFail0 uint64, primes []uint32) { + n, firstFail := big.NewInt(0).SetUint64(n0), big.NewInt(0).SetUint64(firstFail0) + for ; n.Cmp(firstFail) <= 0; n.Add(n, _2) { + prp := true + var a big.Int + for _, a0 := range primes { + a.SetInt64(int64(a0)) + if !ProbablyPrimeBigInt(n, &a) { + prp = false + break + } + } + if prp != IsPrimeUint64(n0) && n0 != firstFail0 { + t.Fatal(n) + } + n0 += 2 + } + } + if !ProbablyPrimeBigInt(big.NewInt(5), _2) { + t.Fatal(false) + } + if !ProbablyPrimeBigInt(big.NewInt(7), _2) { + t.Fatal(false) + } + if ProbablyPrimeBigInt(big.NewInt(9), _2) { + t.Fatal(true) + } + if !ProbablyPrimeBigInt(big.NewInt(11), _2) { + t.Fatal(false) + } + // http://oeis.org/A014233 + f(5, 2047, []uint32{2}) + f(2047, 1373653, []uint32{2, 3}) +} + +var once2059 sync.Once + +func BenchmarkProbablyPrimeBigInt64(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + once2059.Do(func() { b.Log("64 bit n, 64 bit a\n") }) + type t struct { + n, a *big.Int + } + data := make([]t, N) + r := r64() + for i := range data { + var n uint64 + for n <= 3 { + n = uint64(r.Next().Int64()) | 1 + } + var a uint64 + for a <= 1 { + a = uint64(r.Next().Int64()) + } + data[i] = t{big.NewInt(0).SetUint64(n), big.NewInt(0).SetUint64(uint64(a))} + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := data[i&(N-1)] + ProbablyPrimeBigInt(v.n, v.a) + } +} + +var once2090 sync.Once + +func BenchmarkProbablyPrimeBigInt128(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + once2090.Do(func() { b.Log("128 bit n, 128 bit a\n") }) + type t struct { + n, a *big.Int + } + data := make([]t, N) + r := r64() + for i := range data { + n := big.NewInt(0).SetUint64(uint64(r.Next().Int64())) + n.Lsh(n, 64) + n.Add(n, big.NewInt(0).SetUint64(uint64(r.Next().Int64())|1)) + a := big.NewInt(0).SetUint64(uint64(r.Next().Int64())) + a.Lsh(a, 64) + a.Add(a, big.NewInt(0).SetUint64(uint64(r.Next().Int64()))) + data[i] = t{n, a} + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := data[i&(N-1)] + ProbablyPrimeBigInt(v.n, v.a) + } +} + +func TestQCmpUint32(t *testing.T) { + const N = 6e4 + r := r32() + var x, y big.Rat + for i := 0; i < N; i++ { + a, b, c, d := uint32(r.Next()), uint32(r.Next()), uint32(r.Next()), uint32(r.Next()) + x.SetFrac64(int64(a), int64(b)) + y.SetFrac64(int64(c), int64(d)) + if g, e := QCmpUint32(a, b, c, d), x.Cmp(&y); g != e { + t.Fatal(a, b, c, d, g, e) + } + } +} + +func TestQScaleUint32(t *testing.T) { + const N = 4e4 + r := r32() + var x, y big.Rat + var a uint64 + var b, c, d uint32 + for i := 0; i < N; i++ { + for { + b, c, d = uint32(r.Next()), uint32(r.Next()), uint32(r.Next()) + a = QScaleUint32(b, c, d) + if a <= math.MaxInt64 { + break + } + } + x.SetFrac64(int64(a), int64(b)) + y.SetFrac64(int64(c), int64(d)) + if g := x.Cmp(&y); g < 0 { + t.Fatal(a, b, c, d, g, "expexted 1 or 0") + } + + if a != 0 { + x.SetFrac64(int64(a-1), int64(b)) + if g := x.Cmp(&y); g > 0 { + t.Fatal(a, b, c, d, g, "expected -1 or 0") + } + } + } +} + +var smalls = []uint32{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + +func isPrimorialProduct(t FactorTerms, maxp uint32) bool { + if len(t) == 0 { + return false + } + + pmax := uint32(32) + for i, v := range t { + if v.Prime != smalls[i] || v.Power > pmax || v.Power > maxp { + return false + } + pmax = v.Power + } + return true +} + +func TestPrimorialProductsUint32(t *testing.T) { + r := PrimorialProductsUint32(2*3*5*7*11*13*17*19+1, math.MaxUint32, 1) + if len(r) != 1 { + t.Fatal(len(r)) + } + + if r[0] != 2*3*5*7*11*13*17*19*23 { + t.Fatal(r[0]) + } + + r = PrimorialProductsUint32(0, math.MaxUint32, math.MaxUint32) + if g, e := len(r), 1679; g != e { + t.Fatal(g, e) + } + + m := map[uint32]struct{}{} + for _, v := range r { + if _, ok := m[v]; ok { + t.Fatal(v) + } + + m[v] = struct{}{} + } + + for lo := uint32(0); lo < 5e4; lo += 1e3 { + hi := 1e2 * lo + for max := uint32(0); max <= 32; max++ { + m := map[uint32]struct{}{} + for i, v := range PrimorialProductsUint32(lo, hi, max) { + f := FactorInt(v) + if v < lo || v > hi { + t.Fatal(lo, hi, max, v) + } + + if _, ok := m[v]; ok { + t.Fatal(i, lo, hi, max, v, f) + } + + m[v] = struct{}{} + if !isPrimorialProduct(f, max) { + t.Fatal(i, v) + } + + for _, v := range f { + if v.Power > max { + t.Fatal(i, v, f) + } + } + } + } + } +} + +func BenchmarkPrimorialProductsUint32(b *testing.B) { + for i := 0; i < b.N; i++ { + PrimorialProductsUint32(0, math.MaxUint32, math.MaxUint32) + } +} + +func powerizeUint32BigInt(b uint32, n *big.Int) (e uint32, p *big.Int) { + p = big.NewInt(1) + bb := big.NewInt(int64(b)) + for p.Cmp(n) < 0 { + p.Mul(p, bb) + e++ + } + return +} + +func TestPowerizeUint32BigInt(t *testing.T) { + var data = []struct{ b, n, e, p int }{ + {0, 10, 0, -1}, + {1, 10, 0, -1}, + {2, -1, 0, -1}, + {2, 0, 0, 1}, + {2, 1, 0, 1}, + {2, 2, 1, 2}, + {2, 3, 2, 4}, + {3, 0, 0, 1}, + {3, 1, 0, 1}, + {3, 2, 1, 3}, + {3, 3, 1, 3}, + {3, 4, 2, 9}, + {3, 8, 2, 9}, + {3, 9, 2, 9}, + {3, 10, 3, 27}, + {3, 80, 4, 81}, + } + + var n big.Int + for _, v := range data { + b := v.b + n.SetInt64(int64(v.n)) + e, p := PowerizeUint32BigInt(uint32(b), &n) + if e != uint32(v.e) { + t.Fatal(b, &n, e, p, v.e, v.p) + } + + if v.p < 0 { + if p != nil { + t.Fatal(b, &n, e, p, v.e, v.p) + } + continue + } + + if p.Int64() != int64(v.p) { + t.Fatal(b, &n, e, p, v.e, v.p) + } + } + const N = 1e5 + var nn big.Int + for _, base := range []uint32{2, 3, 15, 17} { + for n := 0; n <= N; n++ { + nn.SetInt64(int64(n)) + ge, gp := PowerizeUint32BigInt(base, &nn) + ee, ep := powerizeUint32BigInt(base, &nn) + if ge != ee || gp.Cmp(ep) != 0 { + t.Fatal(base, n, ge, gp, ee, ep) + } + + gp.Div(gp, big.NewInt(int64(base))) + if gp.Sign() > 0 && gp.Cmp(&nn) >= 0 { + t.Fatal(gp.Sign(), gp.Cmp(&nn)) + } + } + } +} + +func benchmarkPowerizeUint32BigInt(b *testing.B, base uint32, exp int) { + b.StopTimer() + var n big.Int + n.SetBit(&n, exp, 1) + b.StartTimer() + for i := 0; i < b.N; i++ { + PowerizeUint32BigInt(base, &n) + } +} + +func BenchmarkPowerizeUint32BigInt_2_2e1e1(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 2, 1e1) +} + +func BenchmarkPowerizeUint32BigInt_2_2e1e2(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 2, 1e2) +} + +func BenchmarkPowerizeUint32BigInt_2_2e1e3(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 2, 1e3) +} + +func BenchmarkPowerizeUint32BigInt_2_2e1e4(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 2, 1e4) +} + +func BenchmarkPowerizeUint32BigInt_2_2e1e5(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 2, 1e5) +} + +func BenchmarkPowerizeUint32BigInt_2_2e1e6(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 2, 1e6) +} + +func BenchmarkPowerizeUint32BigInt_2_2e1e7(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 2, 1e7) +} + +func BenchmarkPowerizeUint32BigInt_3_2e1e1(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 3, 1e1) +} + +func BenchmarkPowerizeUint32BigInt_3_2e1e2(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 3, 1e2) +} + +func BenchmarkPowerizeUint32BigInt_3_2e1e3(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 3, 1e3) +} + +func BenchmarkPowerizeUint32BigInt_3_2e1e4(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 3, 1e4) +} + +func BenchmarkPowerizeUint32BigInt_3_2e1e5(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 3, 1e5) +} + +func BenchmarkPowerizeUint32BigInt_3_2e1e6(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 3, 1e6) +} + +func BenchmarkPowerizeUint32BigInt_15_2e1e1(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 15, 1e1) +} + +func BenchmarkPowerizeUint32BigInt_15_2e1e2(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 15, 1e2) +} + +func BenchmarkPowerizeUint32BigInt_15_2e1e3(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 15, 1e3) +} + +func BenchmarkPowerizeUint32BigInt_15_2e1e4(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 15, 1e4) +} + +func BenchmarkPowerizeUint32BigInt_15_2e1e5(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 15, 1e5) +} + +func BenchmarkPowerizeUint32BigInt_15_2e1e6(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 15, 1e6) +} + +func BenchmarkPowerizeUint32BigInt_17_2e1e1(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 17, 1e1) +} + +func BenchmarkPowerizeUint32BigInt_17_2e1e2(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 17, 1e2) +} + +func BenchmarkPowerizeUint32BigInt_17_2e1e3(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 17, 1e3) +} + +func BenchmarkPowerizeUint32BigInt_17_2e1e4(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 17, 1e4) +} + +func BenchmarkPowerizeUint32BigInt_17_2e1e5(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 17, 1e5) +} + +func BenchmarkPowerizeUint32BigInt_17_2e1e6(b *testing.B) { + benchmarkPowerizeUint32BigInt(b, 17, 1e6) +} + +func TestPowerizeBigInt(t *testing.T) { + var data = []struct{ b, n, e, p int }{ + {0, 10, 0, -1}, + {1, 10, 0, -1}, + {2, -1, 0, -1}, + {2, 0, 0, 1}, + {2, 1, 0, 1}, + {2, 2, 1, 2}, + {2, 3, 2, 4}, + {3, 0, 0, 1}, + {3, 1, 0, 1}, + {3, 2, 1, 3}, + {3, 3, 1, 3}, + {3, 4, 2, 9}, + {3, 8, 2, 9}, + {3, 9, 2, 9}, + {3, 10, 3, 27}, + {3, 80, 4, 81}, + } + + var b, n big.Int + for _, v := range data { + b.SetInt64(int64(v.b)) + n.SetInt64(int64(v.n)) + e, p := PowerizeBigInt(&b, &n) + if e != uint32(v.e) { + t.Fatal(&b, &n, e, p, v.e, v.p) + } + + if v.p < 0 { + if p != nil { + t.Fatal(&b, &n, e, p, v.e, v.p) + } + continue + } + + if p.Int64() != int64(v.p) { + t.Fatal(&b, &n, e, p, v.e, v.p) + } + } + const N = 1e5 + var nn big.Int + for _, base := range []uint32{2, 3, 15, 17} { + b.SetInt64(int64(base)) + for n := 0; n <= N; n++ { + nn.SetInt64(int64(n)) + ge, gp := PowerizeBigInt(&b, &nn) + ee, ep := powerizeUint32BigInt(base, &nn) + if ge != ee || gp.Cmp(ep) != 0 { + t.Fatal(base, n, ge, gp, ee, ep) + } + + gp.Div(gp, &b) + if gp.Sign() > 0 && gp.Cmp(&nn) >= 0 { + t.Fatal(gp.Sign(), gp.Cmp(&nn)) + } + } + } +} + +func benchmarkPowerizeBigInt(b *testing.B, base uint32, exp int) { + b.StopTimer() + var bb, n big.Int + n.SetBit(&n, exp, 1) + bb.SetInt64(int64(base)) + b.StartTimer() + for i := 0; i < b.N; i++ { + PowerizeBigInt(&bb, &n) + } +} + +func BenchmarkPowerizeBigInt_2_2e1e1(b *testing.B) { + benchmarkPowerizeBigInt(b, 2, 1e1) +} + +func BenchmarkPowerizeBigInt_2_2e1e2(b *testing.B) { + benchmarkPowerizeBigInt(b, 2, 1e2) +} + +func BenchmarkPowerizeBigInt_2_2e1e3(b *testing.B) { + benchmarkPowerizeBigInt(b, 2, 1e3) +} + +func BenchmarkPowerizeBigInt_2_2e1e4(b *testing.B) { + benchmarkPowerizeBigInt(b, 2, 1e4) +} + +func BenchmarkPowerizeBigInt_2_2e1e5(b *testing.B) { + benchmarkPowerizeBigInt(b, 2, 1e5) +} + +func BenchmarkPowerizeBigInt_2_2e1e6(b *testing.B) { + benchmarkPowerizeBigInt(b, 2, 1e6) +} + +func BenchmarkPowerizeBigInt_2_2e1e7(b *testing.B) { + benchmarkPowerizeBigInt(b, 2, 1e7) +} + +func BenchmarkPowerizeBigInt_3_2e1e1(b *testing.B) { + benchmarkPowerizeBigInt(b, 3, 1e1) +} + +func BenchmarkPowerizeBigInt_3_2e1e2(b *testing.B) { + benchmarkPowerizeBigInt(b, 3, 1e2) +} + +func BenchmarkPowerizeBigInt_3_2e1e3(b *testing.B) { + benchmarkPowerizeBigInt(b, 3, 1e3) +} + +func BenchmarkPowerizeBigInt_3_2e1e4(b *testing.B) { + benchmarkPowerizeBigInt(b, 3, 1e4) +} + +func BenchmarkPowerizeBigInt_3_2e1e5(b *testing.B) { + benchmarkPowerizeBigInt(b, 3, 1e5) +} + +func BenchmarkPowerizeBigInt_3_2e1e6(b *testing.B) { + benchmarkPowerizeBigInt(b, 3, 1e6) +} + +func BenchmarkPowerizeBigInt_15_2e1e1(b *testing.B) { + benchmarkPowerizeBigInt(b, 15, 1e1) +} + +func BenchmarkPowerizeBigInt_15_2e1e2(b *testing.B) { + benchmarkPowerizeBigInt(b, 15, 1e2) +} + +func BenchmarkPowerizeBigInt_15_2e1e3(b *testing.B) { + benchmarkPowerizeBigInt(b, 15, 1e3) +} + +func BenchmarkPowerizeBigInt_15_2e1e4(b *testing.B) { + benchmarkPowerizeBigInt(b, 15, 1e4) +} + +func BenchmarkPowerizeBigInt_15_2e1e5(b *testing.B) { + benchmarkPowerizeBigInt(b, 15, 1e5) +} + +func BenchmarkPowerizeBigInt_15_2e1e6(b *testing.B) { + benchmarkPowerizeBigInt(b, 15, 1e6) +} + +func BenchmarkPowerizeBigInt_17_2e1e1(b *testing.B) { + benchmarkPowerizeBigInt(b, 17, 1e1) +} + +func BenchmarkPowerizeBigInt_17_2e1e2(b *testing.B) { + benchmarkPowerizeBigInt(b, 17, 1e2) +} + +func BenchmarkPowerizeBigInt_17_2e1e3(b *testing.B) { + benchmarkPowerizeBigInt(b, 17, 1e3) +} + +func BenchmarkPowerizeBigInt_17_2e1e4(b *testing.B) { + benchmarkPowerizeBigInt(b, 17, 1e4) +} + +func BenchmarkPowerizeBigInt_17_2e1e5(b *testing.B) { + benchmarkPowerizeBigInt(b, 17, 1e5) +} + +func BenchmarkPowerizeBigInt_17_2e1e6(b *testing.B) { + benchmarkPowerizeBigInt(b, 17, 1e6) +} + +func TestEnvelope(t *testing.T) { + const prec = 1e-3 + type check struct { + approx Approximation + x, y float64 + } + data := []struct { + points []float64 + checks []check + }{ + { + []float64{0, 1}, + []check{ + {Linear, 0, 0}, + {Linear, 0.25, 0.25}, + {Linear, 0.5, 0.5}, + {Linear, 0.75, 0.75}, + {Linear, 0.9999, 1}, + }, + }, + { + []float64{-1, 0}, + []check{ + {Linear, 0, -1}, + {Linear, 0.25, -0.75}, + {Linear, 0.5, -0.5}, + {Linear, 0.75, -0.25}, + {Linear, 0.9999, 0}, + }, + }, + { + []float64{-1, 1}, + []check{ + {Linear, 0, -1}, + {Linear, 0.25, -0.5}, + {Linear, 0.5, 0}, + {Linear, 0.75, 0.5}, + {Linear, 0.9999, 1}, + }, + }, + { + []float64{-1, 1, -2}, + []check{ + {Linear, 0, -1}, + {Linear, 0.25, 0}, + {Linear, 0.5, 1}, + {Linear, 0.75, -0.5}, + {Linear, 0.9, -1.4}, + {Linear, 0.9999, -2}, + }, + }, + { + []float64{-1, 1}, + []check{ + {Sinusoidal, 0, -1}, + {Sinusoidal, 0.25, -math.Sqrt2 / 2}, + {Sinusoidal, 0.5, 0}, + {Sinusoidal, 0.75, math.Sqrt2 / 2}, + {Sinusoidal, 0.9999, 1}, + }, + }, + { + []float64{-1, 1, -2}, + []check{ + {Sinusoidal, 0, -1}, + {Sinusoidal, 1. / 8, -math.Sqrt2 / 2}, + {Sinusoidal, 2. / 8, 0}, + {Sinusoidal, 3. / 8, math.Sqrt2 / 2}, + {Sinusoidal, 4. / 8, 1}, + {Sinusoidal, 5. / 8, (3*math.Sqrt2 - 2) / 4}, + {Sinusoidal, 6. / 8, -0.5}, + {Sinusoidal, 7. / 8, (-3*math.Sqrt2 - 2) / 4}, + {Sinusoidal, 0.9999, -2}, + }, + }, + } + for i, suite := range data { + for j, test := range suite.checks { + e, g := test.y, Envelope(test.x, suite.points, test.approx) + d := math.Abs(e - g) + if d > prec { + t.Errorf( + "i %d, j %d, x %v, e %v, g %v, d %v, prec %v", + i, j, test.x, e, g, d, prec, + ) + } + } + } +} + +func TestMaxInt(t *testing.T) { + n := int64(MaxInt) + if n != math.MaxInt32 && n != math.MaxInt64 { + t.Error(n) + } + + t.Logf("64 bit ints: %t, MaxInt: %d", n == math.MaxInt64, n) +} + +func TestMinInt(t *testing.T) { + n := int64(MinInt) + if n != math.MinInt32 && n != math.MinInt64 { + t.Error(n) + } + + t.Logf("64 bit ints: %t. MinInt: %d", n == math.MinInt64, n) +} + +func TestMaxUint(t *testing.T) { + n := uint64(MaxUint) + if n != math.MaxUint32 && n != math.MaxUint64 { + t.Error(n) + } + + t.Logf("64 bit uints: %t. MaxUint: %d", n == math.MaxUint64, n) +} + +func TestMax(t *testing.T) { + tests := []struct{ a, b, e int }{ + {MinInt, MinIntM1, MaxInt}, + {MinIntM1, MinInt, MaxInt}, + {MinIntM1, MinIntM1, MaxInt}, + + {MinInt, MinInt, MinInt}, + {MinInt + 1, MinInt, MinInt + 1}, + {MinInt, MinInt + 1, MinInt + 1}, + + {-1, -1, -1}, + {-1, 0, 0}, + {-1, 1, 1}, + + {0, -1, 0}, + {0, 0, 0}, + {0, 1, 1}, + + {1, -1, 1}, + {1, 0, 1}, + {1, 1, 1}, + + {MaxInt, MaxInt, MaxInt}, + {MaxInt - 1, MaxInt, MaxInt}, + {MaxInt, MaxInt - 1, MaxInt}, + + {MaxIntP1, MaxInt, MaxInt}, + {MaxInt, MaxIntP1, MaxInt}, + {MaxIntP1, MaxIntP1, MinInt}, + } + + for _, test := range tests { + if g, e := Max(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMin(t *testing.T) { + tests := []struct{ a, b, e int }{ + {MinIntM1, MinInt, MinInt}, + {MinInt, MinIntM1, MinInt}, + {MinIntM1, MinIntM1, MaxInt}, + + {MinInt, MinInt, MinInt}, + {MinInt + 1, MinInt, MinInt}, + {MinInt, MinInt + 1, MinInt}, + + {-1, -1, -1}, + {-1, 0, -1}, + {-1, 1, -1}, + + {0, -1, -1}, + {0, 0, 0}, + {0, 1, 0}, + + {1, -1, -1}, + {1, 0, 0}, + {1, 1, 1}, + + {MaxInt, MaxInt, MaxInt}, + {MaxInt - 1, MaxInt, MaxInt - 1}, + {MaxInt, MaxInt - 1, MaxInt - 1}, + + {MaxIntP1, MaxInt, MinInt}, + {MaxInt, MaxIntP1, MinInt}, + {MaxIntP1, MaxIntP1, MinInt}, + } + + for _, test := range tests { + if g, e := Min(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestUMax(t *testing.T) { + tests := []struct{ a, b, e uint }{ + {0, 0, 0}, + {0, 1, 1}, + {1, 0, 1}, + + {10, 10, 10}, + {10, 11, 11}, + {11, 10, 11}, + {11, 11, 11}, + + {MaxUint, MaxUint, MaxUint}, + {MaxUint, MaxUint - 1, MaxUint}, + {MaxUint - 1, MaxUint, MaxUint}, + {MaxUint - 1, MaxUint - 1, MaxUint - 1}, + + {MaxUint, MaxUintP1, MaxUint}, + {MaxUintP1, MaxUint, MaxUint}, + {MaxUintP1, MaxUintP1, 0}, + } + + for _, test := range tests { + if g, e := UMax(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestUMin(t *testing.T) { + tests := []struct{ a, b, e uint }{ + {0, 0, 0}, + {0, 1, 0}, + {1, 0, 0}, + + {10, 10, 10}, + {10, 11, 10}, + {11, 10, 10}, + {11, 11, 11}, + + {MaxUint, MaxUint, MaxUint}, + {MaxUint, MaxUint - 1, MaxUint - 1}, + {MaxUint - 1, MaxUint, MaxUint - 1}, + {MaxUint - 1, MaxUint - 1, MaxUint - 1}, + + {MaxUint, MaxUintP1, 0}, + {MaxUintP1, MaxUint, 0}, + {MaxUintP1, MaxUintP1, 0}, + } + + for _, test := range tests { + if g, e := UMin(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMaxByte(t *testing.T) { + tests := []struct{ a, b, e byte }{ + {0, 0, 0}, + {0, 1, 1}, + {1, 0, 1}, + + {10, 10, 10}, + {10, 11, 11}, + {11, 10, 11}, + {11, 11, 11}, + + {math.MaxUint8, math.MaxUint8, math.MaxUint8}, + {math.MaxUint8, math.MaxUint8 - 1, math.MaxUint8}, + {math.MaxUint8 - 1, math.MaxUint8, math.MaxUint8}, + {math.MaxUint8 - 1, math.MaxUint8 - 1, math.MaxUint8 - 1}, + } + + for _, test := range tests { + if g, e := MaxByte(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMinByte(t *testing.T) { + tests := []struct{ a, b, e byte }{ + {0, 0, 0}, + {0, 1, 0}, + {1, 0, 0}, + + {10, 10, 10}, + {10, 11, 10}, + {11, 10, 10}, + {11, 11, 11}, + + {math.MaxUint8, math.MaxUint8, math.MaxUint8}, + {math.MaxUint8, math.MaxUint8 - 1, math.MaxUint8 - 1}, + {math.MaxUint8 - 1, math.MaxUint8, math.MaxUint8 - 1}, + {math.MaxUint8 - 1, math.MaxUint8 - 1, math.MaxUint8 - 1}, + } + + for _, test := range tests { + if g, e := MinByte(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMaxUint16(t *testing.T) { + tests := []struct{ a, b, e uint16 }{ + {0, 0, 0}, + {0, 1, 1}, + {1, 0, 1}, + + {10, 10, 10}, + {10, 11, 11}, + {11, 10, 11}, + {11, 11, 11}, + + {math.MaxUint16, math.MaxUint16, math.MaxUint16}, + {math.MaxUint16, math.MaxUint16 - 1, math.MaxUint16}, + {math.MaxUint16 - 1, math.MaxUint16, math.MaxUint16}, + {math.MaxUint16 - 1, math.MaxUint16 - 1, math.MaxUint16 - 1}, + } + + for _, test := range tests { + if g, e := MaxUint16(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMinUint16(t *testing.T) { + tests := []struct{ a, b, e uint16 }{ + {0, 0, 0}, + {0, 1, 0}, + {1, 0, 0}, + + {10, 10, 10}, + {10, 11, 10}, + {11, 10, 10}, + {11, 11, 11}, + + {math.MaxUint16, math.MaxUint16, math.MaxUint16}, + {math.MaxUint16, math.MaxUint16 - 1, math.MaxUint16 - 1}, + {math.MaxUint16 - 1, math.MaxUint16, math.MaxUint16 - 1}, + {math.MaxUint16 - 1, math.MaxUint16 - 1, math.MaxUint16 - 1}, + } + + for _, test := range tests { + if g, e := MinUint16(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMaxUint32(t *testing.T) { + tests := []struct{ a, b, e uint32 }{ + {0, 0, 0}, + {0, 1, 1}, + {1, 0, 1}, + + {10, 10, 10}, + {10, 11, 11}, + {11, 10, 11}, + {11, 11, 11}, + + {math.MaxUint32, math.MaxUint32, math.MaxUint32}, + {math.MaxUint32, math.MaxUint32 - 1, math.MaxUint32}, + {math.MaxUint32 - 1, math.MaxUint32, math.MaxUint32}, + {math.MaxUint32 - 1, math.MaxUint32 - 1, math.MaxUint32 - 1}, + } + + for _, test := range tests { + if g, e := MaxUint32(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMinUint32(t *testing.T) { + tests := []struct{ a, b, e uint32 }{ + {0, 0, 0}, + {0, 1, 0}, + {1, 0, 0}, + + {10, 10, 10}, + {10, 11, 10}, + {11, 10, 10}, + {11, 11, 11}, + + {math.MaxUint32, math.MaxUint32, math.MaxUint32}, + {math.MaxUint32, math.MaxUint32 - 1, math.MaxUint32 - 1}, + {math.MaxUint32 - 1, math.MaxUint32, math.MaxUint32 - 1}, + {math.MaxUint32 - 1, math.MaxUint32 - 1, math.MaxUint32 - 1}, + } + + for _, test := range tests { + if g, e := MinUint32(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMaxUint64(t *testing.T) { + tests := []struct{ a, b, e uint64 }{ + {0, 0, 0}, + {0, 1, 1}, + {1, 0, 1}, + + {10, 10, 10}, + {10, 11, 11}, + {11, 10, 11}, + {11, 11, 11}, + + {math.MaxUint64, math.MaxUint64, math.MaxUint64}, + {math.MaxUint64, math.MaxUint64 - 1, math.MaxUint64}, + {math.MaxUint64 - 1, math.MaxUint64, math.MaxUint64}, + {math.MaxUint64 - 1, math.MaxUint64 - 1, math.MaxUint64 - 1}, + } + + for _, test := range tests { + if g, e := MaxUint64(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMinUint64(t *testing.T) { + tests := []struct{ a, b, e uint64 }{ + {0, 0, 0}, + {0, 1, 0}, + {1, 0, 0}, + + {10, 10, 10}, + {10, 11, 10}, + {11, 10, 10}, + {11, 11, 11}, + + {math.MaxUint64, math.MaxUint64, math.MaxUint64}, + {math.MaxUint64, math.MaxUint64 - 1, math.MaxUint64 - 1}, + {math.MaxUint64 - 1, math.MaxUint64, math.MaxUint64 - 1}, + {math.MaxUint64 - 1, math.MaxUint64 - 1, math.MaxUint64 - 1}, + } + + for _, test := range tests { + if g, e := MinUint64(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMaxInt8(t *testing.T) { + tests := []struct{ a, b, e int8 }{ + {math.MinInt8, math.MinInt8, math.MinInt8}, + {math.MinInt8 + 1, math.MinInt8, math.MinInt8 + 1}, + {math.MinInt8, math.MinInt8 + 1, math.MinInt8 + 1}, + + {-1, -1, -1}, + {-1, 0, 0}, + {-1, 1, 1}, + + {0, -1, 0}, + {0, 0, 0}, + {0, 1, 1}, + + {1, -1, 1}, + {1, 0, 1}, + {1, 1, 1}, + + {math.MaxInt8, math.MaxInt8, math.MaxInt8}, + {math.MaxInt8 - 1, math.MaxInt8, math.MaxInt8}, + {math.MaxInt8, math.MaxInt8 - 1, math.MaxInt8}, + } + + for _, test := range tests { + if g, e := MaxInt8(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMinInt8(t *testing.T) { + tests := []struct{ a, b, e int8 }{ + {math.MinInt8, math.MinInt8, math.MinInt8}, + {math.MinInt8 + 1, math.MinInt8, math.MinInt8}, + {math.MinInt8, math.MinInt8 + 1, math.MinInt8}, + + {-1, -1, -1}, + {-1, 0, -1}, + {-1, 1, -1}, + + {0, -1, -1}, + {0, 0, 0}, + {0, 1, 0}, + + {1, -1, -1}, + {1, 0, 0}, + {1, 1, 1}, + + {math.MaxInt8, math.MaxInt8, math.MaxInt8}, + {math.MaxInt8 - 1, math.MaxInt8, math.MaxInt8 - 1}, + {math.MaxInt8, math.MaxInt8 - 1, math.MaxInt8 - 1}, + } + + for _, test := range tests { + if g, e := MinInt8(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMaxInt16(t *testing.T) { + tests := []struct{ a, b, e int16 }{ + {math.MinInt16, math.MinInt16, math.MinInt16}, + {math.MinInt16 + 1, math.MinInt16, math.MinInt16 + 1}, + {math.MinInt16, math.MinInt16 + 1, math.MinInt16 + 1}, + + {-1, -1, -1}, + {-1, 0, 0}, + {-1, 1, 1}, + + {0, -1, 0}, + {0, 0, 0}, + {0, 1, 1}, + + {1, -1, 1}, + {1, 0, 1}, + {1, 1, 1}, + + {math.MaxInt16, math.MaxInt16, math.MaxInt16}, + {math.MaxInt16 - 1, math.MaxInt16, math.MaxInt16}, + {math.MaxInt16, math.MaxInt16 - 1, math.MaxInt16}, + } + + for _, test := range tests { + if g, e := MaxInt16(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMinInt16(t *testing.T) { + tests := []struct{ a, b, e int16 }{ + {math.MinInt16, math.MinInt16, math.MinInt16}, + {math.MinInt16 + 1, math.MinInt16, math.MinInt16}, + {math.MinInt16, math.MinInt16 + 1, math.MinInt16}, + + {-1, -1, -1}, + {-1, 0, -1}, + {-1, 1, -1}, + + {0, -1, -1}, + {0, 0, 0}, + {0, 1, 0}, + + {1, -1, -1}, + {1, 0, 0}, + {1, 1, 1}, + + {math.MaxInt16, math.MaxInt16, math.MaxInt16}, + {math.MaxInt16 - 1, math.MaxInt16, math.MaxInt16 - 1}, + {math.MaxInt16, math.MaxInt16 - 1, math.MaxInt16 - 1}, + } + + for _, test := range tests { + if g, e := MinInt16(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMaxInt32(t *testing.T) { + tests := []struct{ a, b, e int32 }{ + {math.MinInt32, math.MinInt32, math.MinInt32}, + {math.MinInt32 + 1, math.MinInt32, math.MinInt32 + 1}, + {math.MinInt32, math.MinInt32 + 1, math.MinInt32 + 1}, + + {-1, -1, -1}, + {-1, 0, 0}, + {-1, 1, 1}, + + {0, -1, 0}, + {0, 0, 0}, + {0, 1, 1}, + + {1, -1, 1}, + {1, 0, 1}, + {1, 1, 1}, + + {math.MaxInt32, math.MaxInt32, math.MaxInt32}, + {math.MaxInt32 - 1, math.MaxInt32, math.MaxInt32}, + {math.MaxInt32, math.MaxInt32 - 1, math.MaxInt32}, + } + + for _, test := range tests { + if g, e := MaxInt32(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMinInt32(t *testing.T) { + tests := []struct{ a, b, e int32 }{ + {math.MinInt32, math.MinInt32, math.MinInt32}, + {math.MinInt32 + 1, math.MinInt32, math.MinInt32}, + {math.MinInt32, math.MinInt32 + 1, math.MinInt32}, + + {-1, -1, -1}, + {-1, 0, -1}, + {-1, 1, -1}, + + {0, -1, -1}, + {0, 0, 0}, + {0, 1, 0}, + + {1, -1, -1}, + {1, 0, 0}, + {1, 1, 1}, + + {math.MaxInt32, math.MaxInt32, math.MaxInt32}, + {math.MaxInt32 - 1, math.MaxInt32, math.MaxInt32 - 1}, + {math.MaxInt32, math.MaxInt32 - 1, math.MaxInt32 - 1}, + } + + for _, test := range tests { + if g, e := MinInt32(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMaxInt64(t *testing.T) { + tests := []struct{ a, b, e int64 }{ + {math.MinInt64, math.MinInt64, math.MinInt64}, + {math.MinInt64 + 1, math.MinInt64, math.MinInt64 + 1}, + {math.MinInt64, math.MinInt64 + 1, math.MinInt64 + 1}, + + {-1, -1, -1}, + {-1, 0, 0}, + {-1, 1, 1}, + + {0, -1, 0}, + {0, 0, 0}, + {0, 1, 1}, + + {1, -1, 1}, + {1, 0, 1}, + {1, 1, 1}, + + {math.MaxInt64, math.MaxInt64, math.MaxInt64}, + {math.MaxInt64 - 1, math.MaxInt64, math.MaxInt64}, + {math.MaxInt64, math.MaxInt64 - 1, math.MaxInt64}, + } + + for _, test := range tests { + if g, e := MaxInt64(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestMinInt64(t *testing.T) { + tests := []struct{ a, b, e int64 }{ + {math.MinInt64, math.MinInt64, math.MinInt64}, + {math.MinInt64 + 1, math.MinInt64, math.MinInt64}, + {math.MinInt64, math.MinInt64 + 1, math.MinInt64}, + + {-1, -1, -1}, + {-1, 0, -1}, + {-1, 1, -1}, + + {0, -1, -1}, + {0, 0, 0}, + {0, 1, 0}, + + {1, -1, -1}, + {1, 0, 0}, + {1, 1, 1}, + + {math.MaxInt64, math.MaxInt64, math.MaxInt64}, + {math.MaxInt64 - 1, math.MaxInt64, math.MaxInt64 - 1}, + {math.MaxInt64, math.MaxInt64 - 1, math.MaxInt64 - 1}, + } + + for _, test := range tests { + if g, e := MinInt64(test.a, test.b), test.e; g != e { + t.Fatal(test.a, test.b, g, e) + } + } +} + +func TestPopCountBigInt(t *testing.T) { + const N = 1e4 + rng := rand.New(rand.NewSource(42)) + lim := big.NewInt(0) + lim.SetBit(lim, 1e3, 1) + z := big.NewInt(0) + m1 := big.NewInt(-1) + for i := 0; i < N; i++ { + z.Rand(rng, lim) + g := PopCountBigInt(z) + e := 0 + for bit := 0; bit < z.BitLen(); bit++ { + if z.Bit(bit) != 0 { + e++ + } + } + if g != e { + t.Fatal(g, e) + } + + z.Mul(z, m1) + if g := PopCountBigInt(z); g != e { + t.Fatal(g, e) + } + } +} + +func benchmarkPopCountBigInt(b *testing.B, bits int) { + lim := big.NewInt(0) + lim.SetBit(lim, bits, 1) + z := big.NewInt(0) + z.Rand(rand.New(rand.NewSource(42)), lim) + b.ResetTimer() + for i := 0; i < b.N; i++ { + PopCountBigInt(z) + } +} + +func BenchmarkPopCountBigInt1e1(b *testing.B) { + benchmarkPopCountBigInt(b, 1e1) +} + +func BenchmarkPopCountBigInt1e2(b *testing.B) { + benchmarkPopCountBigInt(b, 1e2) +} + +func BenchmarkPopCountBigInt1e3(b *testing.B) { + benchmarkPopCountBigInt(b, 1e3) +} + +func BenchmarkPopCountBigIbnt1e4(b *testing.B) { + benchmarkPopCountBigInt(b, 1e4) +} + +func BenchmarkPopCountBigInt1e5(b *testing.B) { + benchmarkPopCountBigInt(b, 1e5) +} + +func BenchmarkPopCountBigInt1e6(b *testing.B) { + benchmarkPopCountBigInt(b, 1e6) +} + +func TestToBase(t *testing.T) { + x := ToBase(big.NewInt(0), 42) + e := []int{0} + if g, e := len(x), len(e); g != e { + t.Fatal(g, e) + } + + for i, g := range x { + if e := e[i]; g != e { + t.Fatal(i, g, e) + } + } + + x = ToBase(big.NewInt(2047), 22) + e = []int{1, 5, 4} + if g, e := len(x), len(e); g != e { + t.Fatal(g, e) + } + + for i, g := range x { + if e := e[i]; g != e { + t.Fatal(i, g, e) + } + } + + x = ToBase(big.NewInt(-2047), 22) + e = []int{-1, -5, -4} + if g, e := len(x), len(e); g != e { + t.Fatal(g, e) + } + + for i, g := range x { + if e := e[i]; g != e { + t.Fatal(i, g, e) + } + } +} + +func TestBug(t *testing.T) { + if BitLenUint(MaxUint) != 64 { + t.Logf("Bug reproducible only on 64 bit architecture") + return + } + + _, err := NewFC32(MinInt, MaxInt, true) + if err == nil { + t.Fatal("Expected non nil err") + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/bits.go b/Godeps/_workspace/src/github.com/cznic/mathutil/bits.go new file mode 100644 index 000000000..6eaa4e305 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/bits.go @@ -0,0 +1,207 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mathutil + +import ( + "math/big" +) + +// BitLenByte returns the bit width of the non zero part of n. +func BitLenByte(n byte) int { + return log2[n] + 1 +} + +// BitLenUint16 returns the bit width of the non zero part of n. +func BitLenUint16(n uint16) int { + if b := n >> 8; b != 0 { + return log2[b] + 8 + 1 + } + + return log2[n] + 1 +} + +// BitLenUint32 returns the bit width of the non zero part of n. +func BitLenUint32(n uint32) int { + if b := n >> 24; b != 0 { + return log2[b] + 24 + 1 + } + + if b := n >> 16; b != 0 { + return log2[b] + 16 + 1 + } + + if b := n >> 8; b != 0 { + return log2[b] + 8 + 1 + } + + return log2[n] + 1 +} + +// BitLen returns the bit width of the non zero part of n. +func BitLen(n int) int { // Should handle correctly [future] 64 bit Go ints + if IntBits == 64 { + return BitLenUint64(uint64(n)) + } + + if b := byte(n >> 24); b != 0 { + return log2[b] + 24 + 1 + } + + if b := byte(n >> 16); b != 0 { + return log2[b] + 16 + 1 + } + + if b := byte(n >> 8); b != 0 { + return log2[b] + 8 + 1 + } + + return log2[byte(n)] + 1 +} + +// BitLenUint returns the bit width of the non zero part of n. +func BitLenUint(n uint) int { // Should handle correctly [future] 64 bit Go uints + if IntBits == 64 { + return BitLenUint64(uint64(n)) + } + + if b := n >> 24; b != 0 { + return log2[b] + 24 + 1 + } + + if b := n >> 16; b != 0 { + return log2[b] + 16 + 1 + } + + if b := n >> 8; b != 0 { + return log2[b] + 8 + 1 + } + + return log2[n] + 1 +} + +// BitLenUint64 returns the bit width of the non zero part of n. +func BitLenUint64(n uint64) int { + if b := n >> 56; b != 0 { + return log2[b] + 56 + 1 + } + + if b := n >> 48; b != 0 { + return log2[b] + 48 + 1 + } + + if b := n >> 40; b != 0 { + return log2[b] + 40 + 1 + } + + if b := n >> 32; b != 0 { + return log2[b] + 32 + 1 + } + + if b := n >> 24; b != 0 { + return log2[b] + 24 + 1 + } + + if b := n >> 16; b != 0 { + return log2[b] + 16 + 1 + } + + if b := n >> 8; b != 0 { + return log2[b] + 8 + 1 + } + + return log2[n] + 1 +} + +// BitLenUintptr returns the bit width of the non zero part of n. +func BitLenUintptr(n uintptr) int { + if b := n >> 56; b != 0 { + return log2[b] + 56 + 1 + } + + if b := n >> 48; b != 0 { + return log2[b] + 48 + 1 + } + + if b := n >> 40; b != 0 { + return log2[b] + 40 + 1 + } + + if b := n >> 32; b != 0 { + return log2[b] + 32 + 1 + } + + if b := n >> 24; b != 0 { + return log2[b] + 24 + 1 + } + + if b := n >> 16; b != 0 { + return log2[b] + 16 + 1 + } + + if b := n >> 8; b != 0 { + return log2[b] + 8 + 1 + } + + return log2[n] + 1 +} + +// PopCountByte returns population count of n (number of bits set in n). +func PopCountByte(n byte) int { + return int(popcnt[byte(n)]) +} + +// PopCountUint16 returns population count of n (number of bits set in n). +func PopCountUint16(n uint16) int { + return int(popcnt[byte(n>>8)]) + int(popcnt[byte(n)]) +} + +// PopCountUint32 returns population count of n (number of bits set in n). +func PopCountUint32(n uint32) int { + return int(popcnt[byte(n>>24)]) + int(popcnt[byte(n>>16)]) + + int(popcnt[byte(n>>8)]) + int(popcnt[byte(n)]) +} + +// PopCount returns population count of n (number of bits set in n). +func PopCount(n int) int { // Should handle correctly [future] 64 bit Go ints + if IntBits == 64 { + return PopCountUint64(uint64(n)) + } + + return PopCountUint32(uint32(n)) +} + +// PopCountUint returns population count of n (number of bits set in n). +func PopCountUint(n uint) int { // Should handle correctly [future] 64 bit Go uints + if IntBits == 64 { + return PopCountUint64(uint64(n)) + } + + return PopCountUint32(uint32(n)) +} + +// PopCountUintptr returns population count of n (number of bits set in n). +func PopCountUintptr(n uintptr) int { + if UintPtrBits == 64 { + return PopCountUint64(uint64(n)) + } + + return PopCountUint32(uint32(n)) +} + +// PopCountUint64 returns population count of n (number of bits set in n). +func PopCountUint64(n uint64) int { + return int(popcnt[byte(n>>56)]) + int(popcnt[byte(n>>48)]) + + int(popcnt[byte(n>>40)]) + int(popcnt[byte(n>>32)]) + + int(popcnt[byte(n>>24)]) + int(popcnt[byte(n>>16)]) + + int(popcnt[byte(n>>8)]) + int(popcnt[byte(n)]) +} + +// PopCountBigInt returns population count of |n| (number of bits set in |n|). +func PopCountBigInt(n *big.Int) (r int) { + for _, v := range n.Bits() { + r += PopCountUintptr(uintptr(v)) + } + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/envelope.go b/Godeps/_workspace/src/github.com/cznic/mathutil/envelope.go new file mode 100644 index 000000000..ff8e6012a --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/envelope.go @@ -0,0 +1,46 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mathutil + +import ( + "math" +) + +// Approximation type determines approximation methods used by e.g. Envelope. +type Approximation int + +// Specific approximation method tags +const ( + _ Approximation = iota + Linear // As named + Sinusoidal // Smooth for all derivations +) + +// Envelope is an utility for defining simple curves using a small (usually) +// set of data points. Envelope returns a value defined by x, points and +// approximation. The value of x must be in [0,1) otherwise the result is +// undefined or the function may panic. Points are interpreted as dividing the +// [0,1) interval in len(points)-1 sections, so len(points) must be > 1 or the +// function may panic. According to the left and right points closing/adjacent +// to the section the resulting value is interpolated using the chosen +// approximation method. Unsupported values of approximation are silently +// interpreted as 'Linear'. +func Envelope(x float64, points []float64, approximation Approximation) float64 { + step := 1 / float64(len(points)-1) + fslot := math.Floor(x / step) + mod := x - fslot*step + slot := int(fslot) + l, r := points[slot], points[slot+1] + rmod := mod / step + switch approximation { + case Sinusoidal: + k := (math.Sin(math.Pi*(rmod-0.5)) + 1) / 2 + return l + (r-l)*k + case Linear: + fallthrough + default: + return l + (r-l)*rmod + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/example/.gitignore b/Godeps/_workspace/src/github.com/cznic/mathutil/example/.gitignore new file mode 100644 index 000000000..ae251f36f --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/example/.gitignore @@ -0,0 +1,2 @@ +example +Makefile diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/example/example.go b/Godeps/_workspace/src/github.com/cznic/mathutil/example/example.go new file mode 100644 index 000000000..30a2d1158 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/example/example.go @@ -0,0 +1,48 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +// +build ignore + +package main + +import ( + "bufio" + "flag" + "github.com/cznic/mathutil" + "log" + "math" + "os" +) + +/* + +$ # Usage e.g.: +$ go run example.go -max 1024 > mathutil.dat # generate 1kB of "random" data + +*/ +func main() { + r, err := mathutil.NewFC32(math.MinInt32, math.MaxInt32, true) + if err != nil { + log.Fatal(err) + } + + var mflag uint64 + flag.Uint64Var(&mflag, "max", 0, "limit output to max bytes") + flag.Parse() + stdout := bufio.NewWriter(os.Stdout) + if mflag != 0 { + for i := uint64(0); i < mflag; i++ { + if err := stdout.WriteByte(byte(r.Next())); err != nil { + log.Fatal(err) + } + } + stdout.Flush() + return + } + + for stdout.WriteByte(byte(r.Next())) == nil { + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/example2/.gitignore b/Godeps/_workspace/src/github.com/cznic/mathutil/example2/.gitignore new file mode 100644 index 000000000..85c399941 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/example2/.gitignore @@ -0,0 +1,2 @@ +example2 +Makefile diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/example2/example2.go b/Godeps/_workspace/src/github.com/cznic/mathutil/example2/example2.go new file mode 100644 index 000000000..409aeef3e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/example2/example2.go @@ -0,0 +1,66 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +// +build ignore + +package main + +import ( + "bytes" + "github.com/cznic/mathutil" + "image" + "image/png" + "io/ioutil" + "log" + "math" + "math/rand" +) + +// $ go run example2.go # view rand.png and rnd.png by your favorite pic viewer +// +// see http://www.boallen.com/random-numbers.html +func main() { + sqr := image.Rect(0, 0, 511, 511) + r, err := mathutil.NewFC32(math.MinInt32, math.MaxInt32, true) + if err != nil { + log.Fatal("NewFC32", err) + } + + img := image.NewGray(sqr) + for y := 0; y < 512; y++ { + for x := 0; x < 512; x++ { + if r.Next()&1 != 0 { + img.Set(x, y, image.White) + } + } + } + buf := bytes.NewBuffer(nil) + if err := png.Encode(buf, img); err != nil { + log.Fatal("Encode rnd.png ", err) + } + + if err := ioutil.WriteFile("rnd.png", buf.Bytes(), 0666); err != nil { + log.Fatal("ioutil.WriteFile/rnd.png ", err) + } + + r2 := rand.New(rand.NewSource(0)) + img = image.NewGray(sqr) + for y := 0; y < 512; y++ { + for x := 0; x < 512; x++ { + if r2.Int()&1 != 0 { + img.Set(x, y, image.White) + } + } + } + buf = bytes.NewBuffer(nil) + if err := png.Encode(buf, img); err != nil { + log.Fatal("Encode rand.png ", err) + } + + if err := ioutil.WriteFile("rand.png", buf.Bytes(), 0666); err != nil { + log.Fatal("ioutil.WriteFile/rand.png ", err) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/example3/.gitignore b/Godeps/_workspace/src/github.com/cznic/mathutil/example3/.gitignore new file mode 100644 index 000000000..474f23a04 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/example3/.gitignore @@ -0,0 +1,2 @@ +example3 +Makefile diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/example3/example3.go b/Godeps/_workspace/src/github.com/cznic/mathutil/example3/example3.go new file mode 100644 index 000000000..572d108b6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/example3/example3.go @@ -0,0 +1,43 @@ +// Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// blame: jnml, labs.nic.cz + +// +build ignore + +package main + +import ( + "bufio" + "flag" + "log" + "math/rand" + "os" +) + +/* + +$ # Usage e.g.: +$ go run example3.go -max 1024 > rand.dat # generate 1kB of "random" data + +*/ +func main() { + r := rand.New(rand.NewSource(1)) + var mflag uint64 + flag.Uint64Var(&mflag, "max", 0, "limit output to max bytes") + flag.Parse() + stdout := bufio.NewWriter(os.Stdout) + if mflag != 0 { + for i := uint64(0); i < mflag; i++ { + if err := stdout.WriteByte(byte(r.Int())); err != nil { + log.Fatal(err) + } + } + stdout.Flush() + return + } + + for stdout.WriteByte(byte(r.Int())) == nil { + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/example4/main.go b/Godeps/_workspace/src/github.com/cznic/mathutil/example4/main.go new file mode 100644 index 000000000..52b35655c --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/example4/main.go @@ -0,0 +1,90 @@ +// Copyright (c) 2011 jnml. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Let QRN be the number of quadratic residues of N. Let Q be QRN/N. From a +// sorted list of primorial products < 2^32 find "record breakers". "Record +// breaker" is N with new lowest Q. +// +// There are only 49 "record breakers" < 2^32. +// +// To run the example $ go run main.go +package main + +import ( + "fmt" + "math" + "sort" + "time" + + "github.com/cznic/mathutil" + "github.com/cznic/sortutil" +) + +func main() { + pp := mathutil.PrimorialProductsUint32(0, math.MaxUint32, 32) + sort.Sort(sortutil.Uint32Slice(pp)) + var bestN, bestD uint32 = 1, 1 + order, checks := 0, 0 + var ixDirty uint32 + m := make([]byte, math.MaxUint32>>3) + for _, n := range pp { + for i := range m[:ixDirty+1] { + m[i] = 0 + } + ixDirty = 0 + checks++ + limit0 := mathutil.QScaleUint32(n, bestN, bestD) + if limit0 > math.MaxUint32 { + panic(0) + } + limit := uint32(limit0) + n64 := uint64(n) + hi := n64 >> 1 + hits := uint32(0) + check := true + fmt.Printf("\r%10d %d/%d", n, checks, len(pp)) + t0 := time.Now() + for i := uint64(0); i < hi; i++ { + sq := uint32(i * i % n64) + ix := sq >> 3 + msk := byte(1 << (sq & 7)) + if m[ix]&msk == 0 { + hits++ + if hits >= limit { + check = false + break + } + } + m[ix] |= msk + if ix > ixDirty { + ixDirty = ix + } + } + + adjPrime := ".." // Composite before + if mathutil.IsPrime(n - 1) { + adjPrime = "P." // Prime before + } + switch mathutil.IsPrime(n + 1) { + case true: + adjPrime += "P" // Prime after + case false: + adjPrime += "." // Composite after + } + + if check && mathutil.QCmpUint32(hits, n, bestN, bestD) < 0 { + order++ + d := time.Since(t0) + bestN, bestD = hits, n + q := float64(hits) / float64(n) + fmt.Printf( + "\r%2s #%03d %d %d %.2f %.2E %s %s %v\n", + adjPrime, order, n, hits, + 1/q, q, d, time.Now().Format("15:04:05"), mathutil.FactorInt(n), + ) + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/example4/results b/Godeps/_workspace/src/github.com/cznic/mathutil/example4/results new file mode 100644 index 000000000..1642c9d85 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/example4/results @@ -0,0 +1,55 @@ +$ time go run main.go +..P #001 2 1 2.00 5.00E-01 3us 11:13:37 +P.. #002 8 3 2.67 3.75E-01 1us 11:13:37 +P.P #003 12 4 3.00 3.33E-01 1us 11:13:37 +..P #004 16 4 4.00 2.50E-01 1us 11:13:37 +P.. #005 32 7 4.57 2.19E-01 1us 11:13:37 +P.. #006 48 8 6.00 1.67E-01 1us 11:13:37 +..P #007 96 14 6.86 1.46E-01 2us 11:13:37 +... #008 144 16 9.00 1.11E-01 2us 11:13:37 +P.P #009 240 24 10.00 1.00E-01 3us 11:13:37 +... #010 288 28 10.29 9.72E-02 4us 11:13:37 +P.. #011 480 42 11.43 8.75E-02 6us 11:13:37 +..P #012 576 48 12.00 8.33E-02 7us 11:13:37 +P.. #013 720 48 15.00 6.67E-02 8us 11:13:37 +P.. #014 1440 84 17.14 5.83E-02 15us 11:13:37 +... #015 1680 96 17.50 5.71E-02 17us 11:13:37 +P.. #016 2880 144 20.00 5.00E-02 28us 11:13:37 +... #017 3600 176 20.45 4.89E-02 35us 11:13:37 +P.. #018 5040 192 26.25 3.81E-02 50us 11:13:37 +P.. #019 10080 336 30.00 3.33E-02 96us 11:13:37 +..P #020 18480 576 32.08 3.12E-02 174us 11:13:37 +..P #021 20160 576 35.00 2.86E-02 190us 11:13:37 +... #022 25200 704 35.80 2.79E-02 237us 11:13:37 +... #023 36960 1008 36.67 2.73E-02 348us 11:13:37 +... #024 50400 1232 40.91 2.44E-02 473us 11:13:37 +P.P #025 55440 1152 48.12 2.08E-02 518us 11:13:37 +P.P #026 110880 2016 55.00 1.82E-02 1.039ms 11:13:37 +... #027 221760 3456 64.17 1.56E-02 2.056ms 11:13:37 +... #028 277200 4224 65.62 1.52E-02 2.82ms 11:13:37 +... #029 443520 6624 66.96 1.49E-02 4.179ms 11:13:37 +... #030 480480 7056 68.10 1.47E-02 4.536ms 11:13:37 +... #031 554400 7392 75.00 1.33E-02 5.217ms 11:13:37 +... #032 720720 8064 89.38 1.12E-02 6.919ms 11:13:37 +P.. #033 1441440 14112 102.14 9.79E-03 14.767ms 11:13:38 +... #034 2882880 24192 119.17 8.39E-03 28.661ms 11:13:38 +... #035 3603600 29568 121.88 8.21E-03 35.55ms 11:13:38 +... #036 5765760 46368 124.35 8.04E-03 57.798ms 11:13:38 +... #037 7207200 51744 139.29 7.18E-03 75.157ms 11:13:38 +... #038 12252240 72576 168.82 5.92E-03 147.179ms 11:13:38 +P.. #039 24504480 127008 192.94 5.18E-03 507.174ms 11:13:40 +... #040 49008960 217728 225.09 4.44E-03 1.334847s 11:13:43 +P.. #041 61261200 266112 230.21 4.34E-03 1.739597s 11:13:45 +... #042 98017920 417312 234.88 4.26E-03 2.971988s 11:13:51 +... #043 122522400 465696 263.10 3.80E-03 3.767685s 11:13:57 +P.P #044 232792560 725760 320.76 3.12E-03 7.425308s 11:14:15 +P.. #045 465585120 1270080 366.58 2.73E-03 15.18066s 11:14:50 +P.. #046 931170240 2177280 427.68 2.34E-03 34.22548s 11:16:06 +..P #047 1163962800 2661120 437.40 2.29E-03 45.038331s 11:17:10 +..P #048 1862340480 4173120 446.27 2.24E-03 1m10.288676s 11:19:26 +... #049 2327925600 4656960 499.88 2.00E-03 1m31.882756s 11:21:44 +4257792000 1679/1679 +real 11m36.548s +user 11m30.530s +sys 0m1.700s +$ diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/ff/main.go b/Godeps/_workspace/src/github.com/cznic/mathutil/ff/main.go new file mode 100644 index 000000000..6cec57afb --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/ff/main.go @@ -0,0 +1,83 @@ +// Copyright (c) jnml. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Factor Finder - searches for Mersenne number factors of one specific special +// form. +package main + +import ( + "flag" + "fmt" + "math/big" + "runtime" + "time" + + "github.com/cznic/mathutil" +) + +const ( + pp = 1 + pp2 = 10 +) + +var ( + _1 = big.NewInt(1) + _2 = big.NewInt(2) +) + +func main() { + runtime.GOMAXPROCS(2) + oClass := flag.Uint64("c", 2, `factor "class" number`) + oDuration := flag.Duration("d", time.Second, "duration to spend on one class") + flag.Parse() + class := *oClass + for class&1 != 0 { + class >>= 1 + } + class = mathutil.MaxUint64(class, 2) + + for { + c := time.After(*oDuration) + factor := big.NewInt(0) + factor.SetUint64(class) + exp := big.NewInt(0) + oneClass: + for { + select { + case <-c: + break oneClass + default: + } + + exp.Set(factor) + factor.Lsh(factor, 1) + factor.Add(factor, _1) + if !factor.ProbablyPrime(pp) { + continue + } + + if !exp.ProbablyPrime(pp) { + continue + } + + if mathutil.ModPowBigInt(_2, exp, factor).Cmp(_1) != 0 { + continue + } + + if !factor.ProbablyPrime(pp2) { + continue + } + + if !exp.ProbablyPrime(pp2) { + continue + } + + fmt.Printf("%d: %s | M%s (%d bits)\n", class, factor, exp, factor.BitLen()) + } + + class += 2 + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/mathutil.go b/Godeps/_workspace/src/github.com/cznic/mathutil/mathutil.go new file mode 100644 index 000000000..e8f3f5622 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/mathutil.go @@ -0,0 +1,829 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package mathutil provides utilities supplementing the standard 'math' and +// 'math/rand' packages. +// +// Compatibility issues +// +// 2013-12-13: The following functions have been REMOVED +// +// func Uint64ToBigInt(n uint64) *big.Int +// func Uint64FromBigInt(n *big.Int) (uint64, bool) +// +// 2013-05-13: The following functions are now DEPRECATED +// +// func Uint64ToBigInt(n uint64) *big.Int +// func Uint64FromBigInt(n *big.Int) (uint64, bool) +// +// These functions will be REMOVED with Go release 1.1+1. +// +// 2013-01-21: The following functions have been REMOVED +// +// func MaxInt() int +// func MinInt() int +// func MaxUint() uint +// func UintPtrBits() int +// +// They are now replaced by untyped constants +// +// MaxInt +// MinInt +// MaxUint +// UintPtrBits +// +// Additionally one more untyped constant was added +// +// IntBits +// +// This change breaks any existing code depending on the above removed +// functions. They should have not been published in the first place, that was +// unfortunate. Instead, defining such architecture and/or implementation +// specific integer limits and bit widths as untyped constants improves +// performance and allows for static dead code elimination if it depends on +// these values. Thanks to minux for pointing it out in the mail list +// (https://groups.google.com/d/msg/golang-nuts/tlPpLW6aJw8/NT3mpToH-a4J). +// +// 2012-12-12: The following functions will be DEPRECATED with Go release +// 1.0.3+1 and REMOVED with Go release 1.0.3+2, b/c of +// http://code.google.com/p/go/source/detail?r=954a79ee3ea8 +// +// func Uint64ToBigInt(n uint64) *big.Int +// func Uint64FromBigInt(n *big.Int) (uint64, bool) +package mathutil + +import ( + "math" + "math/big" +) + +// Architecture and/or implementation specific integer limits and bit widths. +const ( + MaxInt = 1<<(IntBits-1) - 1 + MinInt = -MaxInt - 1 + MaxUint = 1<>32&1 + ^uint(0)>>16&1 + ^uint(0)>>8&1 + 3) + UintPtrBits = 1 << (^uintptr(0)>>32&1 + ^uintptr(0)>>16&1 + ^uintptr(0)>>8&1 + 3) +) + +var ( + _1 = big.NewInt(1) + _2 = big.NewInt(2) +) + +// GCDByte returns the greatest common divisor of a and b. Based on: +// http://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations +func GCDByte(a, b byte) byte { + for b != 0 { + a, b = b, a%b + } + return a +} + +// GCDUint16 returns the greatest common divisor of a and b. +func GCDUint16(a, b uint16) uint16 { + for b != 0 { + a, b = b, a%b + } + return a +} + +// GCD returns the greatest common divisor of a and b. +func GCDUint32(a, b uint32) uint32 { + for b != 0 { + a, b = b, a%b + } + return a +} + +// GCD64 returns the greatest common divisor of a and b. +func GCDUint64(a, b uint64) uint64 { + for b != 0 { + a, b = b, a%b + } + return a +} + +// ISqrt returns floor(sqrt(n)). Typical run time is few hundreds of ns. +func ISqrt(n uint32) (x uint32) { + if n == 0 { + return + } + + if n >= math.MaxUint16*math.MaxUint16 { + return math.MaxUint16 + } + + var px, nx uint32 + for x = n; ; px, x = x, nx { + nx = (x + n/x) / 2 + if nx == x || nx == px { + break + } + } + return +} + +// SqrtUint64 returns floor(sqrt(n)). Typical run time is about 0.5 µs. +func SqrtUint64(n uint64) (x uint64) { + if n == 0 { + return + } + + if n >= math.MaxUint32*math.MaxUint32 { + return math.MaxUint32 + } + + var px, nx uint64 + for x = n; ; px, x = x, nx { + nx = (x + n/x) / 2 + if nx == x || nx == px { + break + } + } + return +} + +// SqrtBig returns floor(sqrt(n)). It panics on n < 0. +func SqrtBig(n *big.Int) (x *big.Int) { + switch n.Sign() { + case -1: + panic(-1) + case 0: + return big.NewInt(0) + } + + var px, nx big.Int + x = big.NewInt(0) + x.SetBit(x, n.BitLen()/2+1, 1) + for { + nx.Rsh(nx.Add(x, nx.Div(n, x)), 1) + if nx.Cmp(x) == 0 || nx.Cmp(&px) == 0 { + break + } + px.Set(x) + x.Set(&nx) + } + return +} + +// Log2Byte returns log base 2 of n. It's the same as index of the highest +// bit set in n. For n == 0 -1 is returned. +func Log2Byte(n byte) int { + return log2[n] +} + +// Log2Uint16 returns log base 2 of n. It's the same as index of the highest +// bit set in n. For n == 0 -1 is returned. +func Log2Uint16(n uint16) int { + if b := n >> 8; b != 0 { + return log2[b] + 8 + } + + return log2[n] +} + +// Log2Uint32 returns log base 2 of n. It's the same as index of the highest +// bit set in n. For n == 0 -1 is returned. +func Log2Uint32(n uint32) int { + if b := n >> 24; b != 0 { + return log2[b] + 24 + } + + if b := n >> 16; b != 0 { + return log2[b] + 16 + } + + if b := n >> 8; b != 0 { + return log2[b] + 8 + } + + return log2[n] +} + +// Log2Uint64 returns log base 2 of n. It's the same as index of the highest +// bit set in n. For n == 0 -1 is returned. +func Log2Uint64(n uint64) int { + if b := n >> 56; b != 0 { + return log2[b] + 56 + } + + if b := n >> 48; b != 0 { + return log2[b] + 48 + } + + if b := n >> 40; b != 0 { + return log2[b] + 40 + } + + if b := n >> 32; b != 0 { + return log2[b] + 32 + } + + if b := n >> 24; b != 0 { + return log2[b] + 24 + } + + if b := n >> 16; b != 0 { + return log2[b] + 16 + } + + if b := n >> 8; b != 0 { + return log2[b] + 8 + } + + return log2[n] +} + +// ModPowByte computes (b^e)%m. It panics for m == 0 || b == e == 0. +// +// See also: http://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method +func ModPowByte(b, e, m byte) byte { + if b == 0 && e == 0 { + panic(0) + } + + if m == 1 { + return 0 + } + + r := uint16(1) + for b, m := uint16(b), uint16(m); e > 0; b, e = b*b%m, e>>1 { + if e&1 == 1 { + r = r * b % m + } + } + return byte(r) +} + +// ModPowByte computes (b^e)%m. It panics for m == 0 || b == e == 0. +func ModPowUint16(b, e, m uint16) uint16 { + if b == 0 && e == 0 { + panic(0) + } + + if m == 1 { + return 0 + } + + r := uint32(1) + for b, m := uint32(b), uint32(m); e > 0; b, e = b*b%m, e>>1 { + if e&1 == 1 { + r = r * b % m + } + } + return uint16(r) +} + +// ModPowUint32 computes (b^e)%m. It panics for m == 0 || b == e == 0. +func ModPowUint32(b, e, m uint32) uint32 { + if b == 0 && e == 0 { + panic(0) + } + + if m == 1 { + return 0 + } + + r := uint64(1) + for b, m := uint64(b), uint64(m); e > 0; b, e = b*b%m, e>>1 { + if e&1 == 1 { + r = r * b % m + } + } + return uint32(r) +} + +// ModPowUint64 computes (b^e)%m. It panics for m == 0 || b == e == 0. +func ModPowUint64(b, e, m uint64) (r uint64) { + if b == 0 && e == 0 { + panic(0) + } + + if m == 1 { + return 0 + } + + return modPowBigInt(big.NewInt(0).SetUint64(b), big.NewInt(0).SetUint64(e), big.NewInt(0).SetUint64(m)).Uint64() +} + +func modPowBigInt(b, e, m *big.Int) (r *big.Int) { + r = big.NewInt(1) + for i, n := 0, e.BitLen(); i < n; i++ { + if e.Bit(i) != 0 { + r.Mod(r.Mul(r, b), m) + } + b.Mod(b.Mul(b, b), m) + } + return +} + +// ModPowBigInt computes (b^e)%m. Returns nil for e < 0. It panics for m == 0 || b == e == 0. +func ModPowBigInt(b, e, m *big.Int) (r *big.Int) { + if b.Sign() == 0 && e.Sign() == 0 { + panic(0) + } + + if m.Cmp(_1) == 0 { + return big.NewInt(0) + } + + if e.Sign() < 0 { + return + } + + return modPowBigInt(big.NewInt(0).Set(b), big.NewInt(0).Set(e), m) +} + +var uint64ToBigIntDelta big.Int + +func init() { + uint64ToBigIntDelta.SetBit(&uint64ToBigIntDelta, 63, 1) +} + +var uintptrBits int + +func init() { + x := uint64(math.MaxUint64) + uintptrBits = BitLenUintptr(uintptr(x)) +} + +// UintptrBits returns the bit width of an uintptr at the executing machine. +func UintptrBits() int { + return uintptrBits +} + +// AddUint128_64 returns the uint128 sum of uint64 a and b. +func AddUint128_64(a, b uint64) (hi uint64, lo uint64) { + lo = a + b + if lo < a { + hi = 1 + } + return +} + +// MulUint128_64 returns the uint128 bit product of uint64 a and b. +func MulUint128_64(a, b uint64) (hi, lo uint64) { + /* + 2^(2 W) ahi bhi + 2^W alo bhi + 2^W ahi blo + alo blo + + FEDCBA98 76543210 FEDCBA98 76543210 + ---- alo*blo ---- + ---- alo*bhi ---- + ---- ahi*blo ---- + ---- ahi*bhi ---- + */ + const w = 32 + const m = 1<>w, b>>w, a&m, b&m + lo = alo * blo + mid1 := alo * bhi + mid2 := ahi * blo + c1, lo := AddUint128_64(lo, mid1<>w+mid2>>w+uint64(c1+c2)) + return +} + +// PowerizeBigInt returns (e, p) such that e is the smallest number for which p +// == b^e is greater or equal n. For n < 0 or b < 2 (0, nil) is returned. +// +// NOTE: Run time for large values of n (above about 2^1e6 ~= 1e300000) can be +// significant and/or unacceptabe. For any smaller values of n the function +// typically performs in sub second time. For "small" values of n (cca bellow +// 2^1e3 ~= 1e300) the same can be easily below 10 µs. +// +// A special (and trivial) case of b == 2 is handled separately and performs +// much faster. +func PowerizeBigInt(b, n *big.Int) (e uint32, p *big.Int) { + switch { + case b.Cmp(_2) < 0 || n.Sign() < 0: + return + case n.Sign() == 0 || n.Cmp(_1) == 0: + return 0, big.NewInt(1) + case b.Cmp(_2) == 0: + p = big.NewInt(0) + e = uint32(n.BitLen() - 1) + p.SetBit(p, int(e), 1) + if p.Cmp(n) < 0 { + p.Mul(p, _2) + e++ + } + return + } + + bw := b.BitLen() + nw := n.BitLen() + p = big.NewInt(1) + var bb, r big.Int + for { + switch p.Cmp(n) { + case -1: + x := uint32((nw - p.BitLen()) / bw) + if x == 0 { + x = 1 + } + e += x + switch x { + case 1: + p.Mul(p, b) + default: + r.Set(_1) + bb.Set(b) + e := x + for { + if e&1 != 0 { + r.Mul(&r, &bb) + } + if e >>= 1; e == 0 { + break + } + + bb.Mul(&bb, &bb) + } + p.Mul(p, &r) + } + case 0, 1: + return + } + } +} + +// PowerizeUint32BigInt returns (e, p) such that e is the smallest number for +// which p == b^e is greater or equal n. For n < 0 or b < 2 (0, nil) is +// returned. +// +// More info: see PowerizeBigInt. +func PowerizeUint32BigInt(b uint32, n *big.Int) (e uint32, p *big.Int) { + switch { + case b < 2 || n.Sign() < 0: + return + case n.Sign() == 0 || n.Cmp(_1) == 0: + return 0, big.NewInt(1) + case b == 2: + p = big.NewInt(0) + e = uint32(n.BitLen() - 1) + p.SetBit(p, int(e), 1) + if p.Cmp(n) < 0 { + p.Mul(p, _2) + e++ + } + return + } + + var bb big.Int + bb.SetInt64(int64(b)) + return PowerizeBigInt(&bb, n) +} + +/* +ProbablyPrimeUint32 returns true if n is prime or n is a pseudoprime to base a. +It implements the Miller-Rabin primality test for one specific value of 'a' and +k == 1. + +Wrt pseudocode shown at +http://en.wikipedia.org/wiki/Miller-Rabin_primality_test#Algorithm_and_running_time + + Input: n > 3, an odd integer to be tested for primality; + Input: k, a parameter that determines the accuracy of the test + Output: composite if n is composite, otherwise probably prime + write n − 1 as 2^s·d with d odd by factoring powers of 2 from n − 1 + LOOP: repeat k times: + pick a random integer a in the range [2, n − 2] + x ← a^d mod n + if x = 1 or x = n − 1 then do next LOOP + for r = 1 .. s − 1 + x ← x^2 mod n + if x = 1 then return composite + if x = n − 1 then do next LOOP + return composite + return probably prime + +... this function behaves like passing 1 for 'k' and additionaly a +fixed/non-random 'a'. Otherwise it's the same algorithm. + +See also: http://mathworld.wolfram.com/Rabin-MillerStrongPseudoprimeTest.html +*/ +func ProbablyPrimeUint32(n, a uint32) bool { + d, s := n-1, 0 + for ; d&1 == 0; d, s = d>>1, s+1 { + } + x := uint64(ModPowUint32(a, d, n)) + if x == 1 || uint32(x) == n-1 { + return true + } + + for ; s > 1; s-- { + if x = x * x % uint64(n); x == 1 { + return false + } + + if uint32(x) == n-1 { + return true + } + } + return false +} + +// ProbablyPrimeUint64_32 returns true if n is prime or n is a pseudoprime to +// base a. It implements the Miller-Rabin primality test for one specific value +// of 'a' and k == 1. See also ProbablyPrimeUint32. +func ProbablyPrimeUint64_32(n uint64, a uint32) bool { + d, s := n-1, 0 + for ; d&1 == 0; d, s = d>>1, s+1 { + } + x := ModPowUint64(uint64(a), d, n) + if x == 1 || x == n-1 { + return true + } + + bx, bn := big.NewInt(0).SetUint64(x), big.NewInt(0).SetUint64(n) + for ; s > 1; s-- { + if x = bx.Mod(bx.Mul(bx, bx), bn).Uint64(); x == 1 { + return false + } + + if x == n-1 { + return true + } + } + return false +} + +// ProbablyPrimeBigInt_32 returns true if n is prime or n is a pseudoprime to +// base a. It implements the Miller-Rabin primality test for one specific value +// of 'a' and k == 1. See also ProbablyPrimeUint32. +func ProbablyPrimeBigInt_32(n *big.Int, a uint32) bool { + var d big.Int + d.Set(n) + d.Sub(&d, _1) // d <- n-1 + s := 0 + for ; d.Bit(s) == 0; s++ { + } + nMinus1 := big.NewInt(0).Set(&d) + d.Rsh(&d, uint(s)) + + x := ModPowBigInt(big.NewInt(int64(a)), &d, n) + if x.Cmp(_1) == 0 || x.Cmp(nMinus1) == 0 { + return true + } + + for ; s > 1; s-- { + if x = x.Mod(x.Mul(x, x), n); x.Cmp(_1) == 0 { + return false + } + + if x.Cmp(nMinus1) == 0 { + return true + } + } + return false +} + +// ProbablyPrimeBigInt returns true if n is prime or n is a pseudoprime to base +// a. It implements the Miller-Rabin primality test for one specific value of +// 'a' and k == 1. See also ProbablyPrimeUint32. +func ProbablyPrimeBigInt(n, a *big.Int) bool { + var d big.Int + d.Set(n) + d.Sub(&d, _1) // d <- n-1 + s := 0 + for ; d.Bit(s) == 0; s++ { + } + nMinus1 := big.NewInt(0).Set(&d) + d.Rsh(&d, uint(s)) + + x := ModPowBigInt(a, &d, n) + if x.Cmp(_1) == 0 || x.Cmp(nMinus1) == 0 { + return true + } + + for ; s > 1; s-- { + if x = x.Mod(x.Mul(x, x), n); x.Cmp(_1) == 0 { + return false + } + + if x.Cmp(nMinus1) == 0 { + return true + } + } + return false +} + +// Max returns the larger of a and b. +func Max(a, b int) int { + if a > b { + return a + } + + return b +} + +// Min returns the smaller of a and b. +func Min(a, b int) int { + if a < b { + return a + } + + return b +} + +// UMax returns the larger of a and b. +func UMax(a, b uint) uint { + if a > b { + return a + } + + return b +} + +// UMin returns the smaller of a and b. +func UMin(a, b uint) uint { + if a < b { + return a + } + + return b +} + +// MaxByte returns the larger of a and b. +func MaxByte(a, b byte) byte { + if a > b { + return a + } + + return b +} + +// MinByte returns the smaller of a and b. +func MinByte(a, b byte) byte { + if a < b { + return a + } + + return b +} + +// MaxInt8 returns the larger of a and b. +func MaxInt8(a, b int8) int8 { + if a > b { + return a + } + + return b +} + +// MinInt8 returns the smaller of a and b. +func MinInt8(a, b int8) int8 { + if a < b { + return a + } + + return b +} + +// MaxUint16 returns the larger of a and b. +func MaxUint16(a, b uint16) uint16 { + if a > b { + return a + } + + return b +} + +// MinUint16 returns the smaller of a and b. +func MinUint16(a, b uint16) uint16 { + if a < b { + return a + } + + return b +} + +// MaxInt16 returns the larger of a and b. +func MaxInt16(a, b int16) int16 { + if a > b { + return a + } + + return b +} + +// MinInt16 returns the smaller of a and b. +func MinInt16(a, b int16) int16 { + if a < b { + return a + } + + return b +} + +// MaxUint32 returns the larger of a and b. +func MaxUint32(a, b uint32) uint32 { + if a > b { + return a + } + + return b +} + +// MinUint32 returns the smaller of a and b. +func MinUint32(a, b uint32) uint32 { + if a < b { + return a + } + + return b +} + +// MaxInt32 returns the larger of a and b. +func MaxInt32(a, b int32) int32 { + if a > b { + return a + } + + return b +} + +// MinInt32 returns the smaller of a and b. +func MinInt32(a, b int32) int32 { + if a < b { + return a + } + + return b +} + +// MaxUint64 returns the larger of a and b. +func MaxUint64(a, b uint64) uint64 { + if a > b { + return a + } + + return b +} + +// MinUint64 returns the smaller of a and b. +func MinUint64(a, b uint64) uint64 { + if a < b { + return a + } + + return b +} + +// MaxInt64 returns the larger of a and b. +func MaxInt64(a, b int64) int64 { + if a > b { + return a + } + + return b +} + +// MinInt64 returns the smaller of a and b. +func MinInt64(a, b int64) int64 { + if a < b { + return a + } + + return b +} + +// ToBase produces n in base b. For example +// +// ToBase(2047, 22) -> [1, 5, 4] +// +// 1 * 22^0 1 +// 5 * 22^1 110 +// 4 * 22^2 1936 +// ---- +// 2047 +// +// ToBase panics for bases < 2. +func ToBase(n *big.Int, b int) []int { + var nn big.Int + nn.Set(n) + if b < 2 { + panic("invalid base") + } + + k := 1 + switch nn.Sign() { + case -1: + nn.Neg(&nn) + k = -1 + case 0: + return []int{0} + } + + bb := big.NewInt(int64(b)) + var r []int + rem := big.NewInt(0) + for nn.Sign() != 0 { + nn.QuoRem(&nn, bb, rem) + r = append(r, k*int(rem.Int64())) + } + return r +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/AUTHORS b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/AUTHORS new file mode 100644 index 000000000..0078f5f5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/AUTHORS @@ -0,0 +1,11 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/CONTRIBUTORS new file mode 100644 index 000000000..5e86f0607 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/CONTRIBUTORS @@ -0,0 +1,9 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/LICENSE b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/LICENSE new file mode 100644 index 000000000..4fa2a1f4f --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The mersenne Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/Makefile b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/Makefile new file mode 100644 index 000000000..a12fc5750 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/Makefile @@ -0,0 +1,24 @@ +# Copyright (c) 2014 The mersenne Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +all: editor + go build + go vet + golint . + go install + make todo + +editor: + go fmt + go test -i + go test + +todo: + @grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alnum:]] *.go || true + @grep -n TODO *.go || true + @grep -n FIXME *.go || true + @grep -n BUG *.go || true + +clean: + rm -f *~ diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/README b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/README new file mode 100644 index 000000000..afa401c71 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/README @@ -0,0 +1,2 @@ +Install: $ go get github.com/cznic/mathutil/mersenne +Godocs: http://gopkgdoc.appspot.com/pkg/github.com/cznic/mathutil/mersenne diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/all_test.go b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/all_test.go new file mode 100644 index 000000000..ab3900af4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/all_test.go @@ -0,0 +1,943 @@ +// Copyright (c) 2014 The mersenne Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mersenne + +import ( + "math" + "math/big" + "math/rand" + "runtime" + "sync" + "testing" + + "github.com/cznic/mathutil" +) + +func r32() *mathutil.FC32 { + r, err := mathutil.NewFC32(math.MinInt32, math.MaxInt32, true) + if err != nil { + panic(err) + } + + return r +} + +var ( + r64lo = big.NewInt(math.MinInt64) + r64hi = big.NewInt(math.MaxInt64) +) + +func r64() *mathutil.FCBig { + r, err := mathutil.NewFCBig(r64lo, r64hi, true) + if err != nil { + panic(err) + } + + return r +} + +func TestNew(t *testing.T) { + const N = 1e4 + data := []struct{ n, m uint32 }{ + {0, 0}, + {1, 1}, + {2, 3}, + {3, 7}, + {4, 15}, + {5, 31}, + {6, 63}, + {7, 127}, + {8, 255}, + {9, 511}, + {10, 1023}, + {11, 2047}, + {12, 4095}, + {13, 8191}, + {14, 16383}, + {15, 32767}, + {16, 65535}, + {17, 131071}, + } + + e := big.NewInt(0) + for _, v := range data { + g := New(v.n) + e.SetInt64(int64(v.m)) + if g.Cmp(e) != 0 { + t.Errorf("%d: got %s, exp %s", v.n, g, e) + } + } + + r := r32() + for i := 0; i < N; i++ { + exp := uint32(r.Next()) % 1e6 + g := New(exp) + b0 := g.BitLen() + g.Add(g, _1) + b1 := g.BitLen() + if b1-b0 != 1 { + t.Fatal(i, exp, b1, b0) + } + } +} + +func benchmarkNew(b *testing.B, max uint32) { + const N = 1 << 16 + b.StopTimer() + a := make([]uint32, N) + r := r32() + for i := range a { + a[i] = uint32(r.Next()) % max + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + New(a[i&(N-1)]) + } +} + +func BenchmarkNew_1e1(b *testing.B) { + benchmarkNew(b, 1e1) +} + +func BenchmarkNew_1e2(b *testing.B) { + benchmarkNew(b, 1e2) +} + +func BenchmarkNew_1e3(b *testing.B) { + benchmarkNew(b, 1e3) +} + +func BenchmarkNew_1e4(b *testing.B) { + benchmarkNew(b, 1e4) +} + +func BenchmarkNew_1e5(b *testing.B) { + benchmarkNew(b, 1e5) +} + +func BenchmarkNew_1e6(b *testing.B) { + benchmarkNew(b, 1e6) +} + +func BenchmarkNew_1e7(b *testing.B) { + benchmarkNew(b, 1e7) +} + +func BenchmarkNew_1e8(b *testing.B) { + benchmarkNew(b, 1e8) +} + +func TestHasFactorUint32(t *testing.T) { + data := []struct { + d, e uint32 + r bool + }{ + {0, 42, false}, + {1, 24, true}, + {2, 22, false}, + {3, 2, true}, + {3, 3, false}, + {3, 4, true}, + {3, 5, false}, + {3, 6, true}, + {5, 4, true}, + {5, 5, false}, + {5, 6, false}, + {5, 7, false}, + {5, 8, true}, + {5, 9, false}, + {5, 10, false}, + {5, 11, false}, + {5, 12, true}, + {7, 3, true}, + {7, 6, true}, + {7, 9, true}, + {9, 6, true}, + {9, 12, true}, + {9, 18, true}, + {11, 10, true}, + {23, 11, true}, + {89, 11, true}, + {47, 23, true}, + {193707721, 67, true}, + {13007, 929, true}, + {264248689, 500471, true}, + {112027889, 1000249, true}, + {252079759, 2000633, true}, + {222054983, 3000743, true}, + {1920355681, 4000741, true}, + {330036367, 5000551, true}, + {1020081431, 6000479, true}, + {840074281, 7000619, true}, + {624031279, 8000401, true}, + {378031207, 9000743, true}, + {380036519, 10000961, true}, + {40001447, 20000723, true}, + } + + for _, v := range data { + if g, e := HasFactorUint32(v.d, v.e), v.r; g != e { + t.Errorf("d %d e %d: got %t, exp %t", v.d, v.e, g, e) + } + } +} + +func TestHasFactorUint64(t *testing.T) { + data := []struct { + d uint64 + e uint32 + r bool + }{ + {0, 42, false}, + {1, 24, true}, + {2, 22, false}, + {3, 2, true}, + {3, 3, false}, + {3, 4, true}, + {3, 5, false}, + {3, 6, true}, + {5, 4, true}, + {5, 5, false}, + {5, 6, false}, + {5, 7, false}, + {5, 8, true}, + {5, 9, false}, + {5, 10, false}, + {5, 11, false}, + {5, 12, true}, + {7, 3, true}, + {7, 6, true}, + {7, 9, true}, + {9, 6, true}, + {9, 12, true}, + {9, 18, true}, + {11, 10, true}, + {23, 11, true}, + {89, 11, true}, + {47, 23, true}, + {193707721, 67, true}, + {13007, 929, true}, + {264248689, 500471, true}, + {112027889, 1000249, true}, + {252079759, 2000633, true}, + {222054983, 3000743, true}, + {1920355681, 4000741, true}, + {330036367, 5000551, true}, + {1020081431, 6000479, true}, + {840074281, 7000619, true}, + {624031279, 8000401, true}, + {378031207, 9000743, true}, + {380036519, 10000961, true}, + {40001447, 20000723, true}, + {1872347344039, 1000099, true}, + } + + for _, v := range data { + if g, e := HasFactorUint64(v.d, v.e), v.r; g != e { + t.Errorf("d %d e %d: got %t, exp %t", v.d, v.e, g, e) + } + } +} + +func TestHasFactorBigInt(t *testing.T) { + data := []struct { + d interface{} + e uint32 + r bool + }{ + {0, 42, false}, + {1, 24, true}, + {2, 22, false}, + {3, 2, true}, + {3, 3, false}, + {3, 4, true}, + {3, 5, false}, + {3, 6, true}, + {5, 4, true}, + {5, 5, false}, + {5, 6, false}, + {5, 7, false}, + {5, 8, true}, + {5, 9, false}, + {5, 10, false}, + {5, 11, false}, + {5, 12, true}, + {7, 3, true}, + {7, 6, true}, + {7, 9, true}, + {9, 6, true}, + {9, 12, true}, + {9, 18, true}, + {11, 10, true}, + {23, 11, true}, + {89, 11, true}, + {47, 23, true}, + {193707721, 67, true}, + {13007, 929, true}, + {264248689, 500471, true}, + {112027889, 1000249, true}, + {252079759, 2000633, true}, + {222054983, 3000743, true}, + {1920355681, 4000741, true}, + {330036367, 5000551, true}, + {1020081431, 6000479, true}, + {840074281, 7000619, true}, + {624031279, 8000401, true}, + {378031207, 9000743, true}, + {380036519, 10000961, true}, + {40001447, 20000723, true}, + {"1872347344039", 1000099, true}, + {"11502865265922183403581252152383", 100279, true}, + {"533975545077050000610542659519277030089249998649", 7293457, true}, + } + + var d big.Int + for _, v := range data { + bigInt(&d, v.d) + if g, e := HasFactorBigInt(&d, v.e), v.r; g != e { + t.Errorf("d %s e %d: got %t, exp %t", &d, v.e, g, e) + } + + if g, e := HasFactorBigInt2(&d, big.NewInt(int64(v.e))), v.r; g != e { + t.Errorf("d %s e %d: got %t, exp %t", &d, v.e, g, e) + } + } +} + +var once309 sync.Once + +func BenchmarkHasFactorUint32Rnd(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct{ d, e uint32 } + a := make([]t, N) + r := r32() + for i := range a { + a[i] = t{ + uint32(r.Next()) | 1, + uint32(r.Next()), + } + } + once309.Do(func() { b.Log("Random 32 bit factor, random 32 bit exponent\n") }) + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + HasFactorUint32(v.d, v.e) + } +} + +var once332 sync.Once + +func BenchmarkHasFactorUint64Rnd(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct { + d uint64 + e uint32 + } + a := make([]t, N) + r := r64() + for i := range a { + a[i] = t{ + uint64(r.Next().Int64()) | 1, + uint32(r.Next().Int64()), + } + } + once332.Do(func() { b.Log("Random 64 bit factor, random 32 bit exponent\n") }) + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + HasFactorUint64(v.d, v.e) + } +} + +var once358 sync.Once + +func BenchmarkHasFactorBigIntRnd_128b(b *testing.B) { + const N = 1 << 16 + b.StopTimer() + type t struct { + d *big.Int + e uint32 + } + a := make([]t, N) + r, err := mathutil.NewFCBig(_1, New(128), true) + if err != nil { + b.Fatal(err) + } + r2 := r32() + for i := range a { + dd := r.Next() + a[i] = t{ + dd.SetBit(dd, 0, 1), + uint32(r2.Next()), + } + } + once358.Do(func() { b.Log("Random 128 bit factor, random 32 bit exponent\n") }) + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + v := a[i&(N-1)] + HasFactorBigInt(v.d, v.e) + } +} + +var ( + f104b, _ = big.NewInt(0).SetString( // 104 bit factor of M100279 + "11502865265922183403581252152383", + 10, + ) + f137b, _ = big.NewInt(0).SetString( // 137 bit factor of M7293457 + "533975545077050000610542659519277030089249998649", + 10, + ) +) + +var once396 sync.Once + +func BenchmarkHasFactorBigInt_104b(b *testing.B) { + b.StopTimer() + once396.Do(func() { b.Log("Verify a 104 bit factor of M100279 (16.6 bit exponent)\n") }) + runtime.GC() + var r bool + b.StartTimer() + for i := 0; i < b.N; i++ { + r = HasFactorBigInt(f104b, 100279) + } + if !r { + b.Fatal(r) + } +} + +var once412 sync.Once + +func BenchmarkHasFactorBigIntMod104b(b *testing.B) { + b.StopTimer() + once412.Do(func() { b.Log("Verify a 104 bit factor of M100279 (16.6 bit exponent) using big.Int.Mod\n") }) + runtime.GC() + m := New(100279) + var x big.Int + b.StartTimer() + for i := 0; i < b.N; i++ { + x.Mod(m, f104b) + } + if x.Cmp(_0) != 0 { + b.Fatal(x) + } +} + +var once429 sync.Once + +func BenchmarkHasFactorBigInt_137b(b *testing.B) { + b.StopTimer() + once429.Do(func() { b.Log("Verify a 137 bit factor of M7293457 (22.8 bit exponent)\n") }) + runtime.GC() + var r bool + b.StartTimer() + for i := 0; i < b.N; i++ { + r = HasFactorBigInt(f137b, 7293457) + } + if !r { + b.Fatal(r) + } +} + +var once445 sync.Once + +func BenchmarkHasFactorBigIntMod137b(b *testing.B) { + b.StopTimer() + once445.Do(func() { b.Log("Verify a 137 bit factor of M7293457 (22.8 bit exponent) using big.Int.Mod\n") }) + runtime.GC() + m := New(7293457) + var x big.Int + b.StartTimer() + for i := 0; i < b.N; i++ { + x.Mod(m, f137b) + } + if x.Cmp(_0) != 0 { + b.Fatal(x) + } +} + +func bigInt(b *big.Int, v interface{}) { + switch v := v.(type) { + case int: + b.SetInt64(int64(v)) + case string: + if _, ok := b.SetString(v, 10); !ok { + panic("bigInt: bad decimal string") + } + default: + panic("bigInt: bad v.(type)") + } +} + +func TestFromFactorBigInt(t *testing.T) { + data := []struct { + d interface{} + n uint32 + }{ + {0, 0}, + {1, 1}, + {2, 0}, + {3, 2}, + {4, 0}, + {5, 4}, + {7, 3}, + {9, 6}, + {11, 10}, + {23, 11}, + {89, 11}, + {"7432339208719", 101}, + {"198582684439", 1009}, + {"20649907789079", 1009}, + {"21624641697047", 1009}, + {"30850253615723594284324529", 1009}, + {"1134327302421596486779379019599", 1009}, + {35311753, 10009}, + {"104272300687", 10009}, + {"10409374085465521", 10009}, + {"890928517778601397463", 10009}, + {6400193, 100003}, + } + + f := func(d *big.Int, max, e uint32) { + if g := FromFactorBigInt(d, max); g != e { + t.Fatalf("%s %d %d %d", d, max, g, e) + } + } + + var d big.Int + for _, v := range data { + bigInt(&d, v.d) + switch { + case v.n > 0: + f(&d, v.n-1, 0) + default: // v.n == 0 + f(&d, 100, 0) + } + f(&d, v.n, v.n) + } +} + +var f20b = big.NewInt(200000447) // 20 bit factor of M100000223 + +func benchmarkFromFactorBigInt(b *testing.B, f *big.Int, max uint32) { + var n uint32 + for i := 0; i < b.N; i++ { + n = FromFactorBigInt(f, max) + } + if n != 0 { + b.Fatal(n) + } +} + +func BenchmarkFromFactorBigInt20b_1e1(b *testing.B) { + benchmarkFromFactorBigInt(b, f20b, 1e1) +} + +func BenchmarkFromFactorBigInt20b_1e2(b *testing.B) { + benchmarkFromFactorBigInt(b, f20b, 1e2) +} + +func BenchmarkFromFactorBigInt20b_1e3(b *testing.B) { + benchmarkFromFactorBigInt(b, f20b, 1e3) +} + +func BenchmarkFromFactorBigInt20b_1e4(b *testing.B) { + benchmarkFromFactorBigInt(b, f20b, 1e4) +} + +func BenchmarkFromFactorBigInt20b_1e5(b *testing.B) { + benchmarkFromFactorBigInt(b, f20b, 1e5) +} + +func BenchmarkFromFactorBigInt20b_1e6(b *testing.B) { + benchmarkFromFactorBigInt(b, f20b, 1e6) +} + +func BenchmarkFromFactorBigInt137b_1e1(b *testing.B) { + benchmarkFromFactorBigInt(b, f137b, 1e1) +} + +func BenchmarkFromFactorBigInt137b_1e2(b *testing.B) { + benchmarkFromFactorBigInt(b, f137b, 1e2) +} + +func BenchmarkFromFactorBigInt137b_1e3(b *testing.B) { + benchmarkFromFactorBigInt(b, f137b, 1e3) +} + +func BenchmarkFromFactorBigInt137b_1e4(b *testing.B) { + benchmarkFromFactorBigInt(b, f137b, 1e4) +} + +func BenchmarkFromFactorBigInt137b_1e5(b *testing.B) { + benchmarkFromFactorBigInt(b, f137b, 1e5) +} + +func BenchmarkFromFactorBigInt137b_1e6(b *testing.B) { + benchmarkFromFactorBigInt(b, f137b, 1e6) +} +func TestMod(t *testing.T) { + const N = 1e4 + data := []struct { + mod, n int64 + exp uint32 + }{ + {0, 0x00, 3}, + {1, 0x01, 3}, + {3, 0x03, 3}, + {0, 0x07, 3}, + {1, 0x0f, 3}, + {3, 0x1f, 3}, + {0, 0x3f, 3}, + {1, 0x7f, 3}, + {3, 0xff, 3}, + {0, 0x1ff, 3}, + } + + var mod, n big.Int + for _, v := range data { + n.SetInt64(v.n) + p := Mod(&mod, &n, v.exp) + if p != &mod { + t.Fatal(p) + } + + if g, e := mod.Int64(), v.mod; g != e { + t.Fatal(v.n, v.exp, g, e) + } + } + + f := func(in int64, exp uint32) { + n.SetInt64(in) + mod.Mod(&n, New(exp)) + e := mod.Int64() + Mod(&mod, &n, exp) + g := mod.Int64() + if g != e { + t.Fatal(in, exp, g, e) + } + } + + r32, _ := mathutil.NewFC32(1, 1e6, true) + r64, _ := mathutil.NewFCBig(_0, big.NewInt(math.MaxInt64), true) + for i := 0; i < N; i++ { + f(r64.Next().Int64(), uint32(r32.Next())) + } +} + +func benchmarkMod(b *testing.B, w, exp uint32) { + b.StopTimer() + var n, mod big.Int + n.Rand(rand.New(rand.NewSource(1)), New(w)) + n.SetBit(&n, int(w), 1) + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + Mod(&mod, &n, exp) + } +} + +func benchmarkModBig(b *testing.B, w, exp uint32) { + b.StopTimer() + var n, mod big.Int + n.Rand(rand.New(rand.NewSource(1)), New(w)) + n.SetBit(&n, int(w), 1) + runtime.GC() + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + mod.Mod(&n, New(exp)) + } +} + +func BenchmarkMod_1e2(b *testing.B) { + benchmarkMod(b, 1e2+2, 1e2) +} + +func BenchmarkModBig_1e2(b *testing.B) { + benchmarkModBig(b, 1e2+2, 1e2) +} + +func BenchmarkMod_1e3(b *testing.B) { + benchmarkMod(b, 1e3+2, 1e3) +} + +func BenchmarkModBig_1e3(b *testing.B) { + benchmarkModBig(b, 1e3+2, 1e3) +} + +func BenchmarkMod_1e4(b *testing.B) { + benchmarkMod(b, 1e4+2, 1e4) +} + +func BenchmarkModBig_1e4(b *testing.B) { + benchmarkModBig(b, 1e4+2, 1e4) +} + +func BenchmarkMod_1e5(b *testing.B) { + benchmarkMod(b, 1e5+2, 1e5) +} + +func BenchmarkModBig_1e5(b *testing.B) { + benchmarkModBig(b, 1e5+2, 1e5) +} + +func BenchmarkMod_1e6(b *testing.B) { + benchmarkMod(b, 1e6+2, 1e6) +} + +func BenchmarkModBig_1e6(b *testing.B) { + benchmarkModBig(b, 1e6+2, 1e6) +} + +func BenchmarkMod_1e7(b *testing.B) { + benchmarkMod(b, 1e7+2, 1e7) +} + +func BenchmarkModBig_1e7(b *testing.B) { + benchmarkModBig(b, 1e7+2, 1e7) +} + +func BenchmarkMod_1e8(b *testing.B) { + benchmarkMod(b, 1e8+2, 1e8) +} + +func BenchmarkModBig_1e8(b *testing.B) { + benchmarkModBig(b, 1e8+2, 1e8) +} + +func BenchmarkMod_5e8(b *testing.B) { + benchmarkMod(b, 5e8+2, 5e8) +} + +func BenchmarkModBig_5e8(b *testing.B) { + benchmarkModBig(b, 5e8+2, 5e8) +} + +func TestModPow(t *testing.T) { + const N = 2e2 + data := []struct{ b, e, m, r uint32 }{ + {0, 1, 1, 0}, + {0, 2, 1, 0}, + {0, 3, 1, 0}, + + {1, 0, 1, 0}, + {1, 1, 1, 0}, + {1, 2, 1, 0}, + {1, 3, 1, 0}, + + {2, 0, 1, 0}, + {2, 1, 1, 0}, + {2, 2, 1, 0}, + {2, 3, 1, 0}, + + {2, 3, 4, 8}, + {2, 3, 5, 4}, + {2, 4, 3, 1}, + {3, 3, 3, 3}, + {3, 4, 5, 30}, + } + + f := func(b, e, m uint32, expect *big.Int) { + got := ModPow(b, e, m) + if got.Cmp(expect) != 0 { + t.Fatal(b, e, m, got, expect) + } + } + + var r big.Int + for _, v := range data { + r.SetInt64(int64(v.r)) + f(v.b, v.e, v.m, &r) + } + + rg, _ := mathutil.NewFC32(2, 1<<10, true) + var bb big.Int + for i := 0; i < N; i++ { + b, e, m := uint32(rg.Next()), uint32(rg.Next()), uint32(rg.Next()) + bb.SetInt64(int64(b)) + f(b, e, m, mathutil.ModPowBigInt(&bb, New(e), New(m))) + } +} + +func benchmarkModPow2(b *testing.B, e, m uint32) { + b.StopTimer() + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + ModPow2(e, m) + } +} + +func benchmarkModPow(b *testing.B, base, e, m uint32) { + b.StopTimer() + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + ModPow(base, e, m) + } +} + +func benchmarkModPowBig(b *testing.B, base, e, m uint32) { + b.StopTimer() + bb := big.NewInt(int64(base)) + ee := New(e) + mm := New(m) + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + mathutil.ModPowBigInt(bb, ee, mm) + } +} + +func BenchmarkModPow2_1e2(b *testing.B) { + benchmarkModPow2(b, 1e2, 1e2+1) +} + +func BenchmarkModPow_2_1e2(b *testing.B) { + benchmarkModPow(b, 2, 1e2, 1e2+1) +} + +func BenchmarkModPowB_2_1e2(b *testing.B) { + benchmarkModPowBig(b, 2, 1e2, 1e2+1) +} + +func BenchmarkModPow_3_1e2(b *testing.B) { + benchmarkModPow(b, 3, 1e2, 1e2+1) +} + +func BenchmarkModPowB_3_1e2(b *testing.B) { + benchmarkModPowBig(b, 3, 1e2, 1e2+1) +} + +// ---- + +func BenchmarkModPow2_1e3(b *testing.B) { + benchmarkModPow2(b, 1e3, 1e3+1) +} + +func BenchmarkModPow_2_1e3(b *testing.B) { + benchmarkModPow(b, 2, 1e3, 1e3+1) +} + +func BenchmarkModPowB_2_1e3(b *testing.B) { + benchmarkModPowBig(b, 2, 1e3, 1e3+1) +} + +func BenchmarkModPow_3_1e3(b *testing.B) { + benchmarkModPow(b, 3, 1e3, 1e3+1) +} + +func BenchmarkModPowB_3_1e3(b *testing.B) { + benchmarkModPowBig(b, 3, 1e3, 1e3+1) +} + +// ---- + +func BenchmarkModPow2_1e4(b *testing.B) { + benchmarkModPow2(b, 1e4, 1e4+1) +} + +func BenchmarkModPow_2_1e4(b *testing.B) { + benchmarkModPow(b, 2, 1e4, 1e4+1) +} + +func BenchmarkModPowB_2_1e4(b *testing.B) { + benchmarkModPowBig(b, 2, 1e4, 1e4+1) +} + +func BenchmarkModPow_3_1e4(b *testing.B) { + benchmarkModPow(b, 3, 1e4, 1e4+1) +} + +func BenchmarkModPowB_3_1e4(b *testing.B) { + benchmarkModPowBig(b, 3, 1e4, 1e4+1) +} + +// ---- + +func BenchmarkModPow2_1e5(b *testing.B) { + benchmarkModPow2(b, 1e5, 1e5+1) +} + +func BenchmarkModPow2_1e6(b *testing.B) { + benchmarkModPow2(b, 1e6, 1e6+1) +} + +func BenchmarkModPow2_1e7(b *testing.B) { + benchmarkModPow2(b, 1e7, 1e7+1) +} + +func BenchmarkModPow2_1e8(b *testing.B) { + benchmarkModPow2(b, 1e8, 1e8+1) +} + +func BenchmarkModPow2_1e9(b *testing.B) { + benchmarkModPow2(b, 1e9, 1e9+1) +} + +func TestModPow2(t *testing.T) { + const N = 1e3 + data := []struct{ e, m uint32 }{ + // e == 0 -> x == 0 + {0, 2}, + {0, 3}, + {0, 4}, + + {1, 2}, + {1, 3}, + {1, 4}, + {1, 5}, + + {2, 2}, + {2, 3}, + {2, 4}, + {2, 5}, + + {3, 2}, + {3, 3}, + {3, 4}, + {3, 5}, + {3, 6}, + {3, 7}, + {3, 8}, + {3, 9}, + + {4, 2}, + {4, 3}, + {4, 4}, + {4, 5}, + {4, 6}, + {4, 7}, + {4, 8}, + {4, 9}, + } + + var got big.Int + f := func(e, m uint32) { + x := ModPow2(e, m) + exp := ModPow(2, e, m) + got.SetInt64(0) + got.SetBit(&got, int(x), 1) + if got.Cmp(exp) != 0 { + t.Fatalf("\ne %d, m %d\ng: %s\ne: %s", e, m, &got, exp) + } + } + + for _, v := range data { + f(v.e, v.m) + } + + rg, _ := mathutil.NewFC32(2, 1<<10, true) + for i := 0; i < N; i++ { + f(uint32(rg.Next()), uint32(rg.Next())) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/mersenne.go b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/mersenne.go new file mode 100644 index 000000000..21b6f396c --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/mersenne/mersenne.go @@ -0,0 +1,296 @@ +// Copyright (c) 2014 The mersenne Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package mersenne collects utilities related to Mersenne numbers[1] and/or some +of their properties. + +Exponent + +In this documentation the term 'exponent' refers to 'n' of a Mersenne number Mn +equal to 2^n-1. This package supports only uint32 sized exponents. New() +currently supports exponents only up to math.MaxInt32 (31 bits, up to 256 MB +required to represent such Mn in memory as a big.Int). + +Links + +Referenced from above: + [1] http://en.wikipedia.org/wiki/Mersenne_number +*/ +package mersenne + +import ( + "math" + "math/big" + + "github.com/cznic/mathutil" + "github.com/remyoudompheng/bigfft" +) + +var ( + _0 = big.NewInt(0) + _1 = big.NewInt(1) + _2 = big.NewInt(2) +) + +// Knowns list the exponent of currently (March 2012) known Mersenne primes +// exponents in order. See also: http://oeis.org/A000043 for a partial list. +var Knowns = []uint32{ + 2, // #1 + 3, // #2 + 5, // #3 + 7, // #4 + 13, // #5 + 17, // #6 + 19, // #7 + 31, // #8 + 61, // #9 + 89, // #10 + + 107, // #11 + 127, // #12 + 521, // #13 + 607, // #14 + 1279, // #15 + 2203, // #16 + 2281, // #17 + 3217, // #18 + 4253, // #19 + 4423, // #20 + + 9689, // #21 + 9941, // #22 + 11213, // #23 + 19937, // #24 + 21701, // #25 + 23209, // #26 + 44497, // #27 + 86243, // #28 + 110503, // #29 + 132049, // #30 + + 216091, // #31 + 756839, // #32 + 859433, // #33 + 1257787, // #34 + 1398269, // #35 + 2976221, // #36 + 3021377, // #37 + 6972593, // #38 + 13466917, // #39 + 20996011, // #40 + + 24036583, // #41 + 25964951, // #42 + 30402457, // #43 + 32582657, // #44 + 37156667, // #45 + 42643801, // #46 + 43112609, // #47 + 57885161, // #48 +} + +// Known maps the exponent of known Mersenne primes its ordinal number/rank. +// Ranks > 41 are currently provisional. +var Known map[uint32]int + +func init() { + Known = map[uint32]int{} + for i, v := range Knowns { + Known[v] = i + 1 + } +} + +// New returns Mn == 2^n-1 for n <= math.MaxInt32 or nil otherwise. +func New(n uint32) (m *big.Int) { + if n > math.MaxInt32 { + return + } + + m = big.NewInt(0) + return m.Sub(m.SetBit(m, int(n), 1), _1) +} + +// HasFactorUint32 returns true if d | Mn. Typical run time for a 32 bit factor +// and a 32 bit exponent is < 1 µs. +func HasFactorUint32(d, n uint32) bool { + return d == 1 || d&1 != 0 && mathutil.ModPowUint32(2, n, d) == 1 +} + +// HasFactorUint64 returns true if d | Mn. Typical run time for a 64 bit factor +// and a 32 bit exponent is < 30 µs. +func HasFactorUint64(d uint64, n uint32) bool { + return d == 1 || d&1 != 0 && mathutil.ModPowUint64(2, uint64(n), d) == 1 +} + +// HasFactorBigInt returns true if d | Mn, d > 0. Typical run time for a 128 +// bit factor and a 32 bit exponent is < 75 µs. +func HasFactorBigInt(d *big.Int, n uint32) bool { + return d.Cmp(_1) == 0 || d.Sign() > 0 && d.Bit(0) == 1 && + mathutil.ModPowBigInt(_2, big.NewInt(int64(n)), d).Cmp(_1) == 0 +} + +// HasFactorBigInt2 returns true if d | Mn, d > 0 +func HasFactorBigInt2(d, n *big.Int) bool { + return d.Cmp(_1) == 0 || d.Sign() > 0 && d.Bit(0) == 1 && + mathutil.ModPowBigInt(_2, n, d).Cmp(_1) == 0 +} + +/* +FromFactorBigInt returns n such that d | Mn if n <= max and d is odd. In other +cases zero is returned. + +It is conjectured that every odd d ∊ N divides infinitely many Mersenne numbers. +The returned n should be the exponent of smallest such Mn. + +NOTE: The computation of n from a given d performs roughly in O(n). It is +thus highly recomended to use the 'max' argument to limit the "searched" +exponent upper bound as appropriate. Otherwise the computation can take a long +time as a large factor can be a divisor of a Mn with exponent above the uint32 +limits. + +The FromFactorBigInt function is a modification of the original Will +Edgington's "reverse method", discussed here: +http://tech.groups.yahoo.com/group/primenumbers/message/15061 +*/ +func FromFactorBigInt(d *big.Int, max uint32) (n uint32) { + if d.Bit(0) == 0 { + return + } + + var m big.Int + for n < max { + m.Add(&m, d) + i := 0 + for ; m.Bit(i) == 1; i++ { + if n == math.MaxUint32 { + return 0 + } + + n++ + } + m.Rsh(&m, uint(i)) + if m.Sign() == 0 { + if n > max { + n = 0 + } + return + } + } + return 0 +} + +// Mod sets mod to n % Mexp and returns mod. It panics for exp == 0 || exp >= +// math.MaxInt32 || n < 0. +func Mod(mod, n *big.Int, exp uint32) *big.Int { + if exp == 0 || exp >= math.MaxInt32 || n.Sign() < 0 { + panic(0) + } + + m := New(exp) + mod.Set(n) + var x big.Int + for mod.BitLen() > int(exp) { + x.Set(mod) + x.Rsh(&x, uint(exp)) + mod.And(mod, m) + mod.Add(mod, &x) + } + if mod.BitLen() == int(exp) && mod.Cmp(m) == 0 { + mod.SetInt64(0) + } + return mod +} + +// ModPow2 returns x such that 2^Me % Mm == 2^x. It panics for m < 2. Typical +// run time is < 1 µs. Use instead of ModPow(2, e, m) wherever possible. +func ModPow2(e, m uint32) (x uint32) { + /* + m < 2 -> panic + e == 0 -> x == 0 + e == 1 -> x == 1 + + 2^M1 % M2 == 2^1 % 3 == 2^1 10 // 2^1, 3, 5, 7 ... +2k + 2^M1 % M3 == 2^1 % 7 == 2^1 010 // 2^1, 4, 7, ... +3k + 2^M1 % M4 == 2^1 % 15 == 2^1 0010 // 2^1, 5, 9, 13... +4k + 2^M1 % M5 == 2^1 % 31 == 2^1 00010 // 2^1, 6, 11, 16... +5k + + 2^M2 % M2 == 2^3 % 3 == 2^1 10.. // 2^3, 5, 7, 9, 11, ... +2k + 2^M2 % M3 == 2^3 % 7 == 2^0 001... // 2^3, 6, 9, 12, 15, ... +3k + 2^M2 % M4 == 2^3 % 15 == 2^3 1000 // 2^3, 7, 11, 15, 19, ... +4k + 2^M2 % M5 == 2^3 % 31 == 2^3 01000 // 2^3, 8, 13, 18, 23, ... +5k + + 2^M3 % M2 == 2^7 % 3 == 2^1 10..--.. // 2^3, 5, 7... +2k + 2^M3 % M3 == 2^7 % 7 == 2^1 010...--- // 2^1, 4, 7... +3k + 2^M3 % M4 == 2^7 % 15 == 2^3 1000.... // +4k + 2^M3 % M5 == 2^7 % 31 == 2^2 00100..... // +5k + 2^M3 % M6 == 2^7 % 63 == 2^1 000010...... // +6k + 2^M3 % M7 == 2^7 % 127 == 2^0 0000001....... + 2^M3 % M8 == 2^7 % 255 == 2^7 10000000 + 2^M3 % M9 == 2^7 % 511 == 2^7 010000000 + + 2^M4 % M2 == 2^15 % 3 == 2^1 10..--..--..--.. + 2^M4 % M3 == 2^15 % 7 == 2^0 1...---...---... + 2^M4 % M4 == 2^15 % 15 == 2^3 1000....----.... + 2^M4 % M5 == 2^15 % 31 == 2^0 1.....-----..... + 2^M4 % M6 == 2^15 % 63 == 2^3 1000......------ + 2^M4 % M7 == 2^15 % 127 == 2^1 10.......------- + 2^M4 % M8 == 2^15 % 255 == 2^7 10000000........ + 2^M4 % M9 == 2^15 % 511 == 2^6 1000000......... + */ + switch { + case m < 2: + panic(0) + case e < 2: + return e + } + + if x = mathutil.ModPowUint32(2, e, m); x == 0 { + return m - 1 + } + + return x - 1 +} + +// ModPow returns b^Me % Mm. Run time grows quickly with 'e' and/or 'm' when b +// != 2 (then ModPow2 is used). +func ModPow(b, e, m uint32) (r *big.Int) { + if m == 1 { + return big.NewInt(0) + } + + if b == 2 { + x := ModPow2(e, m) + r = big.NewInt(0) + r.SetBit(r, int(x), 1) + return + } + + bb := big.NewInt(int64(b)) + r = big.NewInt(1) + for ; e != 0; e-- { + r = bigfft.Mul(r, bb) + Mod(r, r, m) + bb = bigfft.Mul(bb, bb) + Mod(bb, bb, m) + } + return +} + +// ProbablyPrime returns true if Mn is prime or is a pseudoprime to base a. +// Note: Every Mp, prime p, is a prime or is a pseudoprime to base 2, actually +// to every base 2^i, i ∊ [1, p). In contrast - it is conjectured (w/o any +// known counterexamples) that no composite Mp, prime p, is a pseudoprime to +// base 3. +func ProbablyPrime(n, a uint32) bool { + //TODO +test, +bench + if a == 2 { + return ModPow2(n-1, n) == 0 + } + + nMinus1 := New(n) + nMinus1.Sub(nMinus1, _1) + x := ModPow(a, n-1, n) + return x.Cmp(_1) == 0 || x.Cmp(nMinus1) == 0 +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/nist-sts-2-1-1-report b/Godeps/_workspace/src/github.com/cznic/mathutil/nist-sts-2-1-1-report new file mode 100644 index 000000000..20e686c61 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/nist-sts-2-1-1-report @@ -0,0 +1,267 @@ +$ ./example -max 100000000 > rnd.dat +$ ./assess 1000000 + G E N E R A T O R S E L E C T I O N + ______________________________________ + + [0] Input File [1] Linear Congruential + [2] Quadratic Congruential I [3] Quadratic Congruential II + [4] Cubic Congruential [5] XOR + [6] Modular Exponentiation [7] Blum-Blum-Shub + [8] Micali-Schnorr [9] G Using SHA-1 + + Enter Choice: 0 + + + User Prescribed Input File: rnd.dat + + S T A T I S T I C A L T E S T S + _________________________________ + + [01] Frequency [02] Block Frequency + [03] Cumulative Sums [04] Runs + [05] Longest Run of Ones [06] Rank + [07] Discrete Fourier Transform [08] Nonperiodic Template Matchings + [09] Overlapping Template Matchings [10] Universal Statistical + [11] Approximate Entropy [12] Random Excursions + [13] Random Excursions Variant [14] Serial + [15] Linear Complexity + + INSTRUCTIONS + Enter 0 if you DO NOT want to apply all of the + statistical tests to each sequence and 1 if you DO. + + Enter Choice: 1 + + P a r a m e t e r A d j u s t m e n t s + ----------------------------------------- + [1] Block Frequency Test - block length(M): 128 + [2] NonOverlapping Template Test - block length(m): 9 + [3] Overlapping Template Test - block length(m): 9 + [4] Approximate Entropy Test - block length(m): 10 + [5] Serial Test - block length(m): 16 + [6] Linear Complexity Test - block length(M): 500 + + Select Test (0 to continue): 0 + + How many bitstreams? 200 + + Input File Format: + [0] ASCII - A sequence of ASCII 0's and 1's + [1] Binary - Each byte in data file contains 8 bits of data + + Select input mode: 1 + + Statistical Testing In Progress......... + + Statistical Testing Complete!!!!!!!!!!!! + +$ cat experiments/AlgorithmTesting/finalAnalysisReport.txt +------------------------------------------------------------------------------ +RESULTS FOR THE UNIFORMITY OF P-VALUES AND THE PROPORTION OF PASSING SEQUENCES +------------------------------------------------------------------------------ + generator is +------------------------------------------------------------------------------ + C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 P-VALUE PROPORTION STATISTICAL TEST +------------------------------------------------------------------------------ + 28 22 17 19 15 8 24 23 19 25 0.093720 198/200 Frequency + 20 18 24 14 18 17 16 28 21 24 0.504219 199/200 BlockFrequency + 25 22 17 24 19 21 22 15 16 19 0.825505 197/200 CumulativeSums + 27 17 16 22 14 26 14 25 19 20 0.304126 199/200 CumulativeSums + 22 19 14 23 22 22 13 28 13 24 0.224821 199/200 Runs + 20 24 18 21 15 13 22 23 24 20 0.719747 197/200 LongestRun + 22 26 18 22 26 15 17 22 20 12 0.410055 199/200 Rank + 25 22 26 22 20 16 20 20 16 13 0.585209 195/200 FFT + 22 11 15 26 33 24 21 13 14 21 0.013102 197/200 NonOverlappingTemplate + 17 11 16 27 19 24 19 20 28 19 0.219006 200/200 NonOverlappingTemplate + 23 27 24 15 21 11 18 27 15 19 0.162606 197/200 NonOverlappingTemplate + 21 18 13 20 19 23 20 17 26 23 0.749884 197/200 NonOverlappingTemplate + 24 22 24 24 24 21 13 15 17 16 0.494392 196/200 NonOverlappingTemplate + 24 16 23 15 23 18 25 16 18 22 0.699313 199/200 NonOverlappingTemplate + 19 23 21 16 27 18 17 20 18 21 0.859637 198/200 NonOverlappingTemplate + 12 20 16 19 26 14 30 20 24 19 0.141256 198/200 NonOverlappingTemplate + 18 21 17 21 20 14 25 19 24 21 0.859637 198/200 NonOverlappingTemplate + 24 25 21 18 23 15 23 17 16 18 0.749884 199/200 NonOverlappingTemplate + 20 22 22 18 16 22 28 16 14 22 0.574903 198/200 NonOverlappingTemplate + 18 23 22 17 24 25 19 16 23 13 0.626709 199/200 NonOverlappingTemplate + 17 22 14 19 21 21 18 19 24 25 0.842937 198/200 NonOverlappingTemplate + 18 17 26 21 22 15 22 18 21 20 0.883171 197/200 NonOverlappingTemplate + 19 25 16 32 15 19 20 18 16 20 0.236810 199/200 NonOverlappingTemplate + 19 18 15 21 24 22 18 21 20 22 0.964295 200/200 NonOverlappingTemplate + 21 14 17 23 26 19 20 22 20 18 0.834308 196/200 NonOverlappingTemplate + 15 21 17 27 26 23 21 17 24 9 0.129620 198/200 NonOverlappingTemplate + 25 17 19 19 18 22 21 22 21 16 0.951205 196/200 NonOverlappingTemplate + 20 19 24 21 19 24 16 18 17 22 0.946308 197/200 NonOverlappingTemplate + 27 16 19 18 23 19 22 17 22 17 0.807412 197/200 NonOverlappingTemplate + 14 18 21 23 23 20 14 22 20 25 0.719747 198/200 NonOverlappingTemplate + 18 22 19 12 24 25 25 22 18 15 0.474986 198/200 NonOverlappingTemplate + 21 18 23 17 19 18 28 19 20 17 0.825505 198/200 NonOverlappingTemplate + 20 19 15 16 27 20 26 17 20 20 0.657933 198/200 NonOverlappingTemplate + 17 25 21 21 11 19 22 16 27 21 0.401199 198/200 NonOverlappingTemplate + 19 16 15 18 24 19 25 25 19 20 0.769527 199/200 NonOverlappingTemplate + 18 20 20 26 20 12 24 25 19 16 0.524101 198/200 NonOverlappingTemplate + 14 16 18 23 21 21 19 19 28 21 0.668321 197/200 NonOverlappingTemplate + 21 20 23 25 21 22 19 17 14 18 0.875539 197/200 NonOverlappingTemplate + 14 16 29 22 23 13 20 29 17 17 0.099513 197/200 NonOverlappingTemplate + 14 19 27 19 17 23 18 24 20 19 0.709558 199/200 NonOverlappingTemplate + 18 15 21 19 27 22 21 23 17 17 0.779188 198/200 NonOverlappingTemplate + 13 23 13 22 22 23 22 21 21 20 0.689019 199/200 NonOverlappingTemplate + 17 14 26 26 16 21 30 15 21 14 0.096578 199/200 NonOverlappingTemplate + 18 21 24 23 21 13 23 23 19 15 0.719747 197/200 NonOverlappingTemplate + 19 21 14 32 20 15 16 18 24 21 0.202268 199/200 NonOverlappingTemplate + 27 22 20 21 21 14 15 22 14 24 0.474986 196/200 NonOverlappingTemplate + 31 12 25 11 21 18 19 16 24 23 0.050305 197/200 NonOverlappingTemplate + 17 26 20 22 15 27 22 19 12 20 0.383827 199/200 NonOverlappingTemplate + 15 22 14 14 31 15 27 18 23 21 0.078086 194/200 NonOverlappingTemplate + 19 19 14 15 24 21 25 21 20 22 0.788728 197/200 NonOverlappingTemplate + 20 21 19 22 25 18 13 24 28 10 0.153763 195/200 NonOverlappingTemplate + 23 17 21 25 21 20 13 30 14 16 0.196920 196/200 NonOverlappingTemplate + 17 31 17 22 16 15 28 23 11 20 0.050305 197/200 NonOverlappingTemplate + 15 21 26 27 15 18 19 21 18 20 0.605916 198/200 NonOverlappingTemplate + 23 18 15 14 20 21 20 20 20 29 0.554420 200/200 NonOverlappingTemplate + 22 19 19 18 19 17 22 21 31 12 0.311542 199/200 NonOverlappingTemplate + 16 22 23 21 19 19 18 24 21 17 0.960198 197/200 NonOverlappingTemplate + 21 21 17 20 16 23 25 22 18 17 0.917870 200/200 NonOverlappingTemplate + 27 17 17 16 21 20 22 18 21 21 0.859637 197/200 NonOverlappingTemplate + 18 24 15 27 18 21 18 16 24 19 0.657933 199/200 NonOverlappingTemplate + 13 16 21 21 15 25 18 22 29 20 0.326749 198/200 NonOverlappingTemplate + 18 17 23 23 15 19 26 30 11 18 0.125927 198/200 NonOverlappingTemplate + 30 21 18 22 17 21 15 17 21 18 0.544254 195/200 NonOverlappingTemplate + 12 18 19 24 16 24 18 24 28 17 0.311542 199/200 NonOverlappingTemplate + 20 15 23 15 18 30 23 18 17 21 0.410055 196/200 NonOverlappingTemplate + 15 18 23 16 29 21 22 16 19 21 0.544254 200/200 NonOverlappingTemplate + 18 16 27 13 21 22 22 21 16 24 0.534146 199/200 NonOverlappingTemplate + 20 25 18 21 16 21 17 28 21 13 0.484646 200/200 NonOverlappingTemplate + 23 22 13 22 14 20 26 18 19 23 0.574903 197/200 NonOverlappingTemplate + 21 24 25 13 19 22 18 13 24 21 0.504219 199/200 NonOverlappingTemplate + 19 13 18 25 22 15 23 28 19 18 0.410055 195/200 NonOverlappingTemplate + 20 15 27 22 26 26 14 13 21 16 0.181557 198/200 NonOverlappingTemplate + 18 18 19 23 18 20 19 21 24 20 0.991468 200/200 NonOverlappingTemplate + 18 23 17 14 20 25 22 22 22 17 0.816537 198/200 NonOverlappingTemplate + 26 15 15 11 23 21 21 16 36 16 0.005557 196/200 NonOverlappingTemplate + 27 13 21 23 21 16 19 20 16 24 0.544254 198/200 NonOverlappingTemplate + 16 15 32 17 20 23 22 19 20 16 0.262249 200/200 NonOverlappingTemplate + 26 19 24 13 24 16 18 18 13 29 0.137282 199/200 NonOverlappingTemplate + 15 18 14 27 32 21 15 20 19 19 0.112047 198/200 NonOverlappingTemplate + 22 23 22 18 20 23 19 22 16 15 0.924076 196/200 NonOverlappingTemplate + 18 17 21 22 14 17 22 24 20 25 0.798139 199/200 NonOverlappingTemplate + 15 17 19 24 21 23 17 25 23 16 0.739918 196/200 NonOverlappingTemplate + 22 11 15 26 32 25 21 13 14 21 0.017305 197/200 NonOverlappingTemplate + 22 16 19 23 22 21 21 19 17 20 0.985788 200/200 NonOverlappingTemplate + 22 28 18 24 14 20 23 21 20 10 0.230755 198/200 NonOverlappingTemplate + 14 13 22 28 14 28 17 22 23 19 0.129620 197/200 NonOverlappingTemplate + 22 16 22 20 21 21 16 19 18 25 0.935716 198/200 NonOverlappingTemplate + 15 20 23 17 19 22 21 23 18 22 0.951205 200/200 NonOverlappingTemplate + 20 24 21 19 17 19 19 24 15 22 0.930026 198/200 NonOverlappingTemplate + 18 21 15 21 17 28 24 22 20 14 0.534146 200/200 NonOverlappingTemplate + 19 15 19 19 20 20 15 25 23 25 0.779188 198/200 NonOverlappingTemplate + 17 24 25 16 15 21 18 19 23 22 0.788728 198/200 NonOverlappingTemplate + 15 20 18 25 24 15 21 31 18 13 0.141256 200/200 NonOverlappingTemplate + 24 17 19 20 18 21 15 22 24 20 0.924076 196/200 NonOverlappingTemplate + 23 18 17 21 17 28 23 21 18 14 0.605916 197/200 NonOverlappingTemplate + 21 19 22 23 16 17 20 21 22 19 0.985788 200/200 NonOverlappingTemplate + 27 17 21 27 24 15 15 17 15 22 0.304126 199/200 NonOverlappingTemplate + 25 28 20 24 13 14 16 22 19 19 0.304126 197/200 NonOverlappingTemplate + 27 16 14 24 22 18 24 20 18 17 0.564639 196/200 NonOverlappingTemplate + 18 18 24 19 19 19 26 11 27 19 0.375313 195/200 NonOverlappingTemplate + 20 15 29 19 26 16 21 11 18 25 0.141256 197/200 NonOverlappingTemplate + 19 14 21 25 11 23 22 25 26 14 0.176657 199/200 NonOverlappingTemplate + 18 23 20 17 19 18 29 22 26 8 0.102526 199/200 NonOverlappingTemplate + 22 17 18 16 18 20 19 19 25 26 0.834308 198/200 NonOverlappingTemplate + 25 18 14 16 16 24 18 18 30 21 0.268917 198/200 NonOverlappingTemplate + 24 21 23 13 12 22 20 23 20 22 0.554420 196/200 NonOverlappingTemplate + 18 21 21 30 22 17 19 14 18 20 0.534146 197/200 NonOverlappingTemplate + 25 20 22 21 15 18 17 20 17 25 0.825505 199/200 NonOverlappingTemplate + 18 21 22 21 18 20 26 16 20 18 0.941144 197/200 NonOverlappingTemplate + 23 18 22 25 12 16 17 19 26 22 0.474986 198/200 NonOverlappingTemplate + 22 18 29 23 19 23 17 17 15 17 0.534146 198/200 NonOverlappingTemplate + 19 21 17 26 18 15 22 26 15 21 0.626709 197/200 NonOverlappingTemplate + 16 20 20 23 18 21 18 18 25 21 0.955835 199/200 NonOverlappingTemplate + 23 21 20 21 22 10 15 27 15 26 0.186566 198/200 NonOverlappingTemplate + 18 26 20 26 26 18 17 17 20 12 0.358641 198/200 NonOverlappingTemplate + 24 20 21 18 24 12 19 27 14 21 0.401199 195/200 NonOverlappingTemplate + 16 25 15 21 24 18 18 25 22 16 0.657933 199/200 NonOverlappingTemplate + 24 14 17 26 15 17 17 25 21 24 0.428095 200/200 NonOverlappingTemplate + 22 24 11 20 22 24 19 18 12 28 0.176657 196/200 NonOverlappingTemplate + 27 16 27 18 27 14 13 16 21 21 0.141256 197/200 NonOverlappingTemplate + 23 25 20 18 23 17 15 23 19 17 0.834308 196/200 NonOverlappingTemplate + 19 21 20 27 16 16 18 25 16 22 0.678686 199/200 NonOverlappingTemplate + 25 22 21 19 15 19 22 19 25 13 0.657933 197/200 NonOverlappingTemplate + 19 28 21 25 20 12 18 13 29 15 0.073417 198/200 NonOverlappingTemplate + 20 24 21 19 21 15 17 24 20 19 0.941144 198/200 NonOverlappingTemplate + 18 29 23 17 24 19 17 18 16 19 0.585209 200/200 NonOverlappingTemplate + 18 28 18 16 25 21 18 20 14 22 0.544254 198/200 NonOverlappingTemplate + 22 19 23 22 22 21 21 26 12 12 0.401199 199/200 NonOverlappingTemplate + 22 15 25 16 21 27 14 22 21 17 0.484646 199/200 NonOverlappingTemplate + 18 25 20 23 30 17 13 22 18 14 0.213309 200/200 NonOverlappingTemplate + 20 23 21 21 23 29 16 13 16 18 0.410055 199/200 NonOverlappingTemplate + 21 19 16 22 31 18 20 17 18 18 0.514124 198/200 NonOverlappingTemplate + 26 22 12 14 23 17 21 24 21 20 0.455937 197/200 NonOverlappingTemplate + 21 17 18 17 14 32 21 26 18 16 0.162606 197/200 NonOverlappingTemplate + 22 24 22 23 11 15 17 18 29 19 0.230755 198/200 NonOverlappingTemplate + 19 27 20 19 23 15 24 15 21 17 0.657933 198/200 NonOverlappingTemplate + 20 25 16 10 24 13 23 21 21 27 0.149495 200/200 NonOverlappingTemplate + 19 21 21 27 17 17 19 21 21 17 0.904708 200/200 NonOverlappingTemplate + 18 23 15 19 24 21 23 21 13 23 0.719747 198/200 NonOverlappingTemplate + 26 16 28 19 19 18 17 17 16 24 0.474986 199/200 NonOverlappingTemplate + 24 32 17 18 20 13 18 18 19 21 0.236810 195/200 NonOverlappingTemplate + 26 25 18 17 12 19 20 23 21 19 0.585209 196/200 NonOverlappingTemplate + 18 26 25 12 18 16 24 19 18 24 0.410055 199/200 NonOverlappingTemplate + 27 21 22 27 21 14 18 14 23 13 0.219006 197/200 NonOverlappingTemplate + 18 23 24 16 19 21 16 26 20 17 0.798139 199/200 NonOverlappingTemplate + 19 30 15 27 14 19 24 11 22 19 0.073417 198/200 NonOverlappingTemplate + 20 23 22 20 22 15 22 21 18 17 0.964295 198/200 NonOverlappingTemplate + 22 31 16 26 13 19 17 22 24 10 0.037566 197/200 NonOverlappingTemplate + 18 24 22 14 23 19 16 18 19 27 0.637119 197/200 NonOverlappingTemplate + 19 20 21 22 21 18 19 22 20 18 0.999438 198/200 NonOverlappingTemplate + 27 15 21 18 28 18 15 23 18 17 0.375313 195/200 NonOverlappingTemplate + 26 23 20 20 23 19 20 23 14 12 0.514124 199/200 NonOverlappingTemplate + 18 19 11 15 21 24 20 26 23 23 0.428095 198/200 NonOverlappingTemplate + 19 16 21 25 19 21 15 24 24 16 0.749884 197/200 NonOverlappingTemplate + 17 26 23 18 20 26 23 14 18 15 0.494392 198/200 NonOverlappingTemplate + 15 17 19 24 21 23 17 25 23 16 0.739918 196/200 NonOverlappingTemplate + 26 19 20 20 24 22 22 13 14 20 0.605916 198/200 OverlappingTemplate + 29 24 17 21 18 13 18 21 17 22 0.446556 196/200 Universal + 22 18 22 20 20 21 22 21 18 16 0.992952 198/200 ApproximateEntropy + 14 8 13 9 11 13 13 8 7 10 0.719747 106/106 RandomExcursions + 13 18 9 7 12 12 9 6 12 8 0.236810 104/106 RandomExcursions + 11 15 10 7 11 14 9 6 12 11 0.595549 106/106 RandomExcursions + 15 7 12 12 9 11 16 8 10 6 0.350485 106/106 RandomExcursions + 10 10 12 16 10 12 10 7 13 6 0.554420 106/106 RandomExcursions + 8 7 12 10 11 16 11 13 10 8 0.657933 106/106 RandomExcursions + 9 6 12 12 14 9 11 13 10 10 0.816537 104/106 RandomExcursions + 10 10 7 12 11 9 10 13 14 10 0.911413 105/106 RandomExcursions + 8 8 12 9 10 5 13 12 17 12 0.319084 104/106 RandomExcursionsVariant + 5 11 10 11 7 11 10 15 11 15 0.455937 104/106 RandomExcursionsVariant + 6 12 11 8 12 12 12 13 13 7 0.699313 104/106 RandomExcursionsVariant + 14 10 11 6 12 9 8 12 11 13 0.779188 104/106 RandomExcursionsVariant + 12 12 10 7 17 6 6 12 13 11 0.262249 103/106 RandomExcursionsVariant + 13 8 14 13 7 6 6 13 15 11 0.249284 102/106 RandomExcursionsVariant + 12 12 12 13 7 9 6 13 12 10 0.739918 105/106 RandomExcursionsVariant + 13 15 12 8 9 10 6 9 14 10 0.574903 106/106 RandomExcursionsVariant + 10 15 9 12 14 10 8 11 7 10 0.739918 105/106 RandomExcursionsVariant + 13 12 8 11 12 11 9 10 11 9 0.978072 103/106 RandomExcursionsVariant + 10 13 12 12 8 13 8 9 14 7 0.739918 104/106 RandomExcursionsVariant + 12 10 10 14 7 8 7 13 14 11 0.657933 106/106 RandomExcursionsVariant + 10 13 10 10 13 10 12 6 10 12 0.897763 106/106 RandomExcursionsVariant + 9 12 15 8 13 8 12 8 11 10 0.779188 106/106 RandomExcursionsVariant + 9 13 15 10 10 10 8 14 6 11 0.616305 106/106 RandomExcursionsVariant + 7 17 9 12 9 11 10 16 4 11 0.129620 106/106 RandomExcursionsVariant + 10 9 10 15 7 12 7 8 12 16 0.419021 106/106 RandomExcursionsVariant + 9 12 11 8 8 9 15 12 9 13 0.798139 106/106 RandomExcursionsVariant + 17 34 11 22 22 17 19 20 13 25 0.026057 199/200 Serial + 22 20 16 22 20 18 20 18 23 21 0.989786 199/200 Serial + 12 33 25 29 21 11 21 15 14 19 0.003996 199/200 LinearComplexity + + +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +The minimum pass rate for each statistical test with the exception of the +random excursion (variant) test is approximately = 193 for a +sample size = 200 binary sequences. + +The minimum pass rate for the random excursion (variant) test +is approximately = 101 for a sample size = 106 binary sequences. + +For further guidelines construct a probability table using the MAPLE program +provided in the addendum section of the documentation. +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +$ diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/permute.go b/Godeps/_workspace/src/github.com/cznic/mathutil/permute.go new file mode 100644 index 000000000..bf828b694 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/permute.go @@ -0,0 +1,39 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mathutil + +import ( + "sort" +) + +// Generate the first permutation of data. +func PermutationFirst(data sort.Interface) { + sort.Sort(data) +} + +// Generate the next permutation of data if possible and return true. +// Return false if there is no more permutation left. +// Based on the algorithm described here: +// http://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order +func PermutationNext(data sort.Interface) bool { + var k, l int + for k = data.Len() - 2; ; k-- { // 1. + if k < 0 { + return false + } + + if data.Less(k, k+1) { + break + } + } + for l = data.Len() - 1; !data.Less(k, l); l-- { // 2. + } + data.Swap(k, l) // 3. + for i, j := k+1, data.Len()-1; i < j; i++ { // 4. + data.Swap(i, j) + j-- + } + return true +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/primes.go b/Godeps/_workspace/src/github.com/cznic/mathutil/primes.go new file mode 100644 index 000000000..bd10fe6d3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/primes.go @@ -0,0 +1,335 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mathutil + +import ( + "math" +) + +// IsPrimeUint16 returns true if n is prime. Typical run time is few ns. +func IsPrimeUint16(n uint16) bool { + return n > 0 && primes16[n-1] == 1 +} + +// NextPrimeUint16 returns first prime > n and true if successful or an +// undefined value and false if there is no next prime in the uint16 limits. +// Typical run time is few ns. +func NextPrimeUint16(n uint16) (p uint16, ok bool) { + return n + uint16(primes16[n]), n < 65521 +} + +// IsPrime returns true if n is prime. Typical run time is about 100 ns. +// +//TODO rename to IsPrimeUint32 +func IsPrime(n uint32) bool { + switch { + case n&1 == 0: + return n == 2 + case n%3 == 0: + return n == 3 + case n%5 == 0: + return n == 5 + case n%7 == 0: + return n == 7 + case n%11 == 0: + return n == 11 + case n%13 == 0: + return n == 13 + case n%17 == 0: + return n == 17 + case n%19 == 0: + return n == 19 + case n%23 == 0: + return n == 23 + case n%29 == 0: + return n == 29 + case n%31 == 0: + return n == 31 + case n%37 == 0: + return n == 37 + case n%41 == 0: + return n == 41 + case n%43 == 0: + return n == 43 + case n%47 == 0: + return n == 47 + case n%53 == 0: + return n == 53 // Benchmarked optimum + case n < 65536: + // use table data + return IsPrimeUint16(uint16(n)) + default: + mod := ModPowUint32(2, (n+1)/2, n) + if mod != 2 && mod != n-2 { + return false + } + blk := &lohi[n>>24] + lo, hi := blk.lo, blk.hi + for lo <= hi { + index := (lo + hi) >> 1 + liar := liars[index] + switch { + case n > liar: + lo = index + 1 + case n < liar: + hi = index - 1 + default: + return false + } + } + return true + } +} + +// IsPrimeUint64 returns true if n is prime. Typical run time is few tens of µs. +// +// SPRP bases: http://miller-rabin.appspot.com +func IsPrimeUint64(n uint64) bool { + switch { + case n%2 == 0: + return n == 2 + case n%3 == 0: + return n == 3 + case n%5 == 0: + return n == 5 + case n%7 == 0: + return n == 7 + case n%11 == 0: + return n == 11 + case n%13 == 0: + return n == 13 + case n%17 == 0: + return n == 17 + case n%19 == 0: + return n == 19 + case n%23 == 0: + return n == 23 + case n%29 == 0: + return n == 29 + case n%31 == 0: + return n == 31 + case n%37 == 0: + return n == 37 + case n%41 == 0: + return n == 41 + case n%43 == 0: + return n == 43 + case n%47 == 0: + return n == 47 + case n%53 == 0: + return n == 53 + case n%59 == 0: + return n == 59 + case n%61 == 0: + return n == 61 + case n%67 == 0: + return n == 67 + case n%71 == 0: + return n == 71 + case n%73 == 0: + return n == 73 + case n%79 == 0: + return n == 79 + case n%83 == 0: + return n == 83 + case n%89 == 0: + return n == 89 // Benchmarked optimum + case n <= math.MaxUint16: + return IsPrimeUint16(uint16(n)) + case n <= math.MaxUint32: + return ProbablyPrimeUint32(uint32(n), 11000544) && + ProbablyPrimeUint32(uint32(n), 31481107) + case n < 105936894253: + return ProbablyPrimeUint64_32(n, 2) && + ProbablyPrimeUint64_32(n, 1005905886) && + ProbablyPrimeUint64_32(n, 1340600841) + case n < 31858317218647: + return ProbablyPrimeUint64_32(n, 2) && + ProbablyPrimeUint64_32(n, 642735) && + ProbablyPrimeUint64_32(n, 553174392) && + ProbablyPrimeUint64_32(n, 3046413974) + case n < 3071837692357849: + return ProbablyPrimeUint64_32(n, 2) && + ProbablyPrimeUint64_32(n, 75088) && + ProbablyPrimeUint64_32(n, 642735) && + ProbablyPrimeUint64_32(n, 203659041) && + ProbablyPrimeUint64_32(n, 3613982119) + default: + return ProbablyPrimeUint64_32(n, 2) && + ProbablyPrimeUint64_32(n, 325) && + ProbablyPrimeUint64_32(n, 9375) && + ProbablyPrimeUint64_32(n, 28178) && + ProbablyPrimeUint64_32(n, 450775) && + ProbablyPrimeUint64_32(n, 9780504) && + ProbablyPrimeUint64_32(n, 1795265022) + } +} + +// NextPrime returns first prime > n and true if successful or an undefined value and false if there +// is no next prime in the uint32 limits. Typical run time is about 2 µs. +// +//TODO rename to NextPrimeUint32 +func NextPrime(n uint32) (p uint32, ok bool) { + switch { + case n < 65521: + p16, _ := NextPrimeUint16(uint16(n)) + return uint32(p16), true + case n >= math.MaxUint32-4: + return + } + + n++ + var d0, d uint32 + switch mod := n % 6; mod { + case 0: + d0, d = 1, 4 + case 1: + d = 4 + case 2, 3, 4: + d0, d = 5-mod, 2 + case 5: + d = 2 + } + + p = n + d0 + if p < n { // overflow + return + } + + for { + if IsPrime(p) { + return p, true + } + + p0 := p + p += d + if p < p0 { // overflow + break + } + + d ^= 6 + } + return +} + +// NextPrimeUint64 returns first prime > n and true if successful or an undefined value and false if there +// is no next prime in the uint64 limits. Typical run time is in hundreds of µs. +func NextPrimeUint64(n uint64) (p uint64, ok bool) { + switch { + case n < 65521: + p16, _ := NextPrimeUint16(uint16(n)) + return uint64(p16), true + case n >= 18446744073709551557: // last uint64 prime + return + } + + n++ + var d0, d uint64 + switch mod := n % 6; mod { + case 0: + d0, d = 1, 4 + case 1: + d = 4 + case 2, 3, 4: + d0, d = 5-mod, 2 + case 5: + d = 2 + } + + p = n + d0 + if p < n { // overflow + return + } + + for { + if ok = IsPrimeUint64(p); ok { + break + } + + p0 := p + p += d + if p < p0 { // overflow + break + } + + d ^= 6 + } + return +} + +// FactorTerm is one term of an integer factorization. +type FactorTerm struct { + Prime uint32 // The divisor + Power uint32 // Term == Prime^Power +} + +// FactorTerms represent a factorization of an integer +type FactorTerms []FactorTerm + +// FactorInt returns prime factorization of n > 1 or nil otherwise. +// Resulting factors are ordered by Prime. Typical run time is few µs. +func FactorInt(n uint32) (f FactorTerms) { + switch { + case n < 2: + return + case IsPrime(n): + return []FactorTerm{{n, 1}} + } + + f, w := make([]FactorTerm, 9), 0 + for p := 2; p < len(primes16); p += int(primes16[p]) { + if uint(p*p) > uint(n) { + break + } + + power := uint32(0) + for n%uint32(p) == 0 { + n /= uint32(p) + power++ + } + if power != 0 { + f[w] = FactorTerm{uint32(p), power} + w++ + } + if n == 1 { + break + } + } + if n != 1 { + f[w] = FactorTerm{n, 1} + w++ + } + return f[:w] +} + +// PrimorialProductsUint32 returns a slice of numbers in [lo, hi] which are a +// product of max 'max' primorials. The slice is not sorted. +// +// See also: http://en.wikipedia.org/wiki/Primorial +func PrimorialProductsUint32(lo, hi, max uint32) (r []uint32) { + lo64, hi64 := int64(lo), int64(hi) + if max > 31 { // N/A + max = 31 + } + + var f func(int64, int64, uint32) + f = func(n, p int64, emax uint32) { + e := uint32(1) + for n <= hi64 && e <= emax { + n *= p + if n >= lo64 && n <= hi64 { + r = append(r, uint32(n)) + } + if n < hi64 { + p, _ := NextPrime(uint32(p)) + f(n, int64(p), e) + } + e++ + } + } + + f(1, 2, max) + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/rat.go b/Godeps/_workspace/src/github.com/cznic/mathutil/rat.go new file mode 100644 index 000000000..91b1c6fb1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/rat.go @@ -0,0 +1,27 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mathutil + +// QCmpUint32 compares a/b and c/d and returns: +// +// -1 if a/b < c/d +// 0 if a/b == c/d +// +1 if a/b > c/d +// +func QCmpUint32(a, b, c, d uint32) int { + switch x, y := uint64(a)*uint64(d), uint64(b)*uint64(c); { + case x < y: + return -1 + case x == y: + return 0 + default: // x > y + return 1 + } +} + +// QScaleUint32 returns a such that a/b >= c/d. +func QScaleUint32(b, c, d uint32) (a uint64) { + return 1 + (uint64(b)*uint64(c))/uint64(d) +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/rnd.go b/Godeps/_workspace/src/github.com/cznic/mathutil/rnd.go new file mode 100644 index 000000000..9132dc0d5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/rnd.go @@ -0,0 +1,383 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mathutil + +import ( + "fmt" + "math" + "math/big" +) + +// FC32 is a full cycle PRNG covering the 32 bit signed integer range. +// In contrast to full cycle generators shown at e.g. http://en.wikipedia.org/wiki/Full_cycle, +// this code doesn't produce values at constant delta (mod cycle length). +// The 32 bit limit is per this implementation, the algorithm used has no intrinsic limit on the cycle size. +// Properties include: +// - Adjustable limits on creation (hi, lo). +// - Positionable/randomly accessible (Pos, Seek). +// - Repeatable (deterministic). +// - Can run forward or backward (Next, Prev). +// - For a billion numbers cycle the Next/Prev PRN can be produced in cca 100-150ns. +// That's like 5-10 times slower compared to PRNs generated using the (non FC) rand package. +type FC32 struct { + cycle int64 // On average: 3 * delta / 2, (HQ: 2 * delta) + delta int64 // hi - lo + factors [][]int64 // This trades some space for hopefully a bit of speed (multiple adding vs multiplying). + lo int + mods []int // pos % set + pos int64 // Within cycle. + primes []int64 // Ordered. ∏ primes == cycle. + set []int64 // Reordered primes (magnitude order bases) according to seed. +} + +// NewFC32 returns a newly created FC32 adjusted for the closed interval [lo, hi] or an Error if any. +// If hq == true then trade some generation time for improved (pseudo)randomness. +func NewFC32(lo, hi int, hq bool) (r *FC32, err error) { + if lo > hi { + return nil, fmt.Errorf("invalid range %d > %d", lo, hi) + } + + if uint64(hi)-uint64(lo) > math.MaxUint32 { + return nil, fmt.Errorf("range out of int32 limits %d, %d", lo, hi) + } + + delta := int64(hi) - int64(lo) + // Find the primorial covering whole delta + n, set, p := int64(1), []int64{}, uint32(2) + if hq { + p++ + } + for { + set = append(set, int64(p)) + n *= int64(p) + if n > delta { + break + } + p, _ = NextPrime(p) + } + + // Adjust the set so n ∊ [delta, 2 * delta] (HQ: [delta, 3 * delta]) + // while keeping the cardinality of the set (correlates with the statistic "randomness quality") + // at max, i.e. discard atmost one member. + i := -1 // no candidate prime + if n > 2*(delta+1) { + for j, p := range set { + q := n / p + if q < delta+1 { + break + } + + i = j // mark the highest candidate prime set index + } + } + if i >= 0 { // shrink the inner cycle + n = n / set[i] + set = delete(set, i) + } + r = &FC32{ + cycle: n, + delta: delta, + factors: make([][]int64, len(set)), + lo: lo, + mods: make([]int, len(set)), + primes: set, + } + r.Seed(1) // the default seed should be always non zero + return +} + +// Cycle reports the length of the inner FCPRNG cycle. +// Cycle is atmost the double (HQ: triple) of the generator period (hi - lo + 1). +func (r *FC32) Cycle() int64 { + return r.cycle +} + +// Next returns the first PRN after Pos. +func (r *FC32) Next() int { + return r.step(1) +} + +// Pos reports the current position within the inner cycle. +func (r *FC32) Pos() int64 { + return r.pos +} + +// Prev return the first PRN before Pos. +func (r *FC32) Prev() int { + return r.step(-1) +} + +// Seed uses the provided seed value to initialize the generator to a deterministic state. +// A zero seed produces a "canonical" generator with worse randomness than for most non zero seeds. +// Still, the FC property holds for any seed value. +func (r *FC32) Seed(seed int64) { + u := uint64(seed) + r.set = mix(r.primes, &u) + n := int64(1) + for i, p := range r.set { + k := make([]int64, p) + v := int64(0) + for j := range k { + k[j] = v + v += n + } + n *= p + r.factors[i] = mix(k, &u) + } +} + +// Seek sets Pos to |pos| % Cycle. +func (r *FC32) Seek(pos int64) { //vet:ignore + if pos < 0 { + pos = -pos + } + pos %= r.cycle + r.pos = pos + for i, p := range r.set { + r.mods[i] = int(pos % p) + } +} + +func (r *FC32) step(dir int) int { + for { // avg loops per step: 3/2 (HQ: 2) + y := int64(0) + pos := r.pos + pos += int64(dir) + switch { + case pos < 0: + pos = r.cycle - 1 + case pos >= r.cycle: + pos = 0 + } + r.pos = pos + for i, mod := range r.mods { + mod += dir + p := int(r.set[i]) + switch { + case mod < 0: + mod = p - 1 + case mod >= p: + mod = 0 + } + r.mods[i] = mod + y += r.factors[i][mod] + } + if y <= r.delta { + return int(y) + r.lo + } + } +} + +func delete(set []int64, i int) (y []int64) { + for j, v := range set { + if j != i { + y = append(y, v) + } + } + return +} + +func mix(set []int64, seed *uint64) (y []int64) { + for len(set) != 0 { + *seed = rol(*seed) + i := int(*seed % uint64(len(set))) + y = append(y, set[i]) + set = delete(set, i) + } + return +} + +func rol(u uint64) (y uint64) { + y = u << 1 + if int64(u) < 0 { + y |= 1 + } + return +} + +// FCBig is a full cycle PRNG covering ranges outside of the int32 limits. +// For more info see the FC32 docs. +// Next/Prev PRN on a 1e15 cycle can be produced in about 2 µsec. +type FCBig struct { + cycle *big.Int // On average: 3 * delta / 2, (HQ: 2 * delta) + delta *big.Int // hi - lo + factors [][]*big.Int // This trades some space for hopefully a bit of speed (multiple adding vs multiplying). + lo *big.Int + mods []int // pos % set + pos *big.Int // Within cycle. + primes []int64 // Ordered. ∏ primes == cycle. + set []int64 // Reordered primes (magnitude order bases) according to seed. +} + +// NewFCBig returns a newly created FCBig adjusted for the closed interval [lo, hi] or an Error if any. +// If hq == true then trade some generation time for improved (pseudo)randomness. +func NewFCBig(lo, hi *big.Int, hq bool) (r *FCBig, err error) { + if lo.Cmp(hi) > 0 { + return nil, fmt.Errorf("invalid range %d > %d", lo, hi) + } + + delta := big.NewInt(0) + delta.Add(delta, hi).Sub(delta, lo) + + // Find the primorial covering whole delta + n, set, pp, p := big.NewInt(1), []int64{}, big.NewInt(0), uint32(2) + if hq { + p++ + } + for { + set = append(set, int64(p)) + pp.SetInt64(int64(p)) + n.Mul(n, pp) + if n.Cmp(delta) > 0 { + break + } + p, _ = NextPrime(p) + } + + // Adjust the set so n ∊ [delta, 2 * delta] (HQ: [delta, 3 * delta]) + // while keeping the cardinality of the set (correlates with the statistic "randomness quality") + // at max, i.e. discard atmost one member. + dd1 := big.NewInt(1) + dd1.Add(dd1, delta) + dd2 := big.NewInt(0) + dd2.Lsh(dd1, 1) + i := -1 // no candidate prime + if n.Cmp(dd2) > 0 { + q := big.NewInt(0) + for j, p := range set { + pp.SetInt64(p) + q.Set(n) + q.Div(q, pp) + if q.Cmp(dd1) < 0 { + break + } + + i = j // mark the highest candidate prime set index + } + } + if i >= 0 { // shrink the inner cycle + pp.SetInt64(set[i]) + n.Div(n, pp) + set = delete(set, i) + } + r = &FCBig{ + cycle: n, + delta: delta, + factors: make([][]*big.Int, len(set)), + lo: lo, + mods: make([]int, len(set)), + pos: big.NewInt(0), + primes: set, + } + r.Seed(1) // the default seed should be always non zero + return +} + +// Cycle reports the length of the inner FCPRNG cycle. +// Cycle is atmost the double (HQ: triple) of the generator period (hi - lo + 1). +func (r *FCBig) Cycle() *big.Int { + return r.cycle +} + +// Next returns the first PRN after Pos. +func (r *FCBig) Next() *big.Int { + return r.step(1) +} + +// Pos reports the current position within the inner cycle. +func (r *FCBig) Pos() *big.Int { + return r.pos +} + +// Prev return the first PRN before Pos. +func (r *FCBig) Prev() *big.Int { + return r.step(-1) +} + +// Seed uses the provided seed value to initialize the generator to a deterministic state. +// A zero seed produces a "canonical" generator with worse randomness than for most non zero seeds. +// Still, the FC property holds for any seed value. +func (r *FCBig) Seed(seed int64) { + u := uint64(seed) + r.set = mix(r.primes, &u) + n := big.NewInt(1) + v := big.NewInt(0) + pp := big.NewInt(0) + for i, p := range r.set { + k := make([]*big.Int, p) + v.SetInt64(0) + for j := range k { + k[j] = big.NewInt(0) + k[j].Set(v) + v.Add(v, n) + } + pp.SetInt64(p) + n.Mul(n, pp) + r.factors[i] = mixBig(k, &u) + } +} + +// Seek sets Pos to |pos| % Cycle. +func (r *FCBig) Seek(pos *big.Int) { + r.pos.Set(pos) + r.pos.Abs(r.pos) + r.pos.Mod(r.pos, r.cycle) + mod := big.NewInt(0) + pp := big.NewInt(0) + for i, p := range r.set { + pp.SetInt64(p) + r.mods[i] = int(mod.Mod(r.pos, pp).Int64()) + } +} + +func (r *FCBig) step(dir int) (y *big.Int) { + y = big.NewInt(0) + d := big.NewInt(int64(dir)) + for { // avg loops per step: 3/2 (HQ: 2) + r.pos.Add(r.pos, d) + switch { + case r.pos.Sign() < 0: + r.pos.Add(r.pos, r.cycle) + case r.pos.Cmp(r.cycle) >= 0: + r.pos.SetInt64(0) + } + for i, mod := range r.mods { + mod += dir + p := int(r.set[i]) + switch { + case mod < 0: + mod = p - 1 + case mod >= p: + mod = 0 + } + r.mods[i] = mod + y.Add(y, r.factors[i][mod]) + } + if y.Cmp(r.delta) <= 0 { + y.Add(y, r.lo) + return + } + y.SetInt64(0) + } +} + +func deleteBig(set []*big.Int, i int) (y []*big.Int) { + for j, v := range set { + if j != i { + y = append(y, v) + } + } + return +} + +func mixBig(set []*big.Int, seed *uint64) (y []*big.Int) { + for len(set) != 0 { + *seed = rol(*seed) + i := int(*seed % uint64(len(set))) + y = append(y, set[i]) + set = deleteBig(set, i) + } + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/tables.go b/Godeps/_workspace/src/github.com/cznic/mathutil/tables.go new file mode 100644 index 000000000..f32952c00 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/tables.go @@ -0,0 +1,6995 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// "Static" data + +package mathutil + +var ( + // Set bits count in a byte + popcnt = [256]byte{ + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, // 0 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 1 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 2 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 3 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 4 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 5 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 6 + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 7 + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 8 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 9 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 10 + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 11 + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 12 + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 13 + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 14 + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, // 15 + } + + // Highest set bit index in a byte + log2 = [256]int{ + -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 0 + + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 1 + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 2 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 3 + + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 4 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 5 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 6 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 7 + + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 8 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 9 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 10 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 11 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 12 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 13 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 14 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 15 + } + + // "Predivisors": 2-53 + liars = [3660]uint32{ + 31621, 42799, 49141, 49981, 65077, 65281, 80581, 83333, 88357, 90751, + 104653, 130561, 164737, 188057, 194221, 196093, 215749, 219781, 220729, 253241, + 256999, 271951, 280601, 282133, 357761, 390937, 458989, 486737, 489997, 514447, + 580337, 587861, 611701, 647089, 653333, 657901, 665281, 665333, 688213, 710533, + 721801, 722261, 738541, 741751, 742813, 745889, 769757, 818201, 838861, 873181, + 877099, 916327, 976873, 983401, 1016801, 1018921, 1053761, 1064053, 1073021, 1082401, + 1109461, 1132657, 1145257, 1168513, 1194649, 1207361, 1251949, 1252697, 1302451, 1325843, + 1357441, 1373653, 1397419, 1441091, 1493857, 1507963, 1509709, 1530787, 1584133, 1678541, + 1690501, 1730977, 1735841, 1811573, 1876393, 1969417, 1987021, 2004403, 2081713, 2163001, + 2181961, 2205967, 2261953, 2264369, 2269093, 2284453, 2304167, 2387797, 2487941, 2510569, + 2670361, 2746477, 2748023, 2757241, 2811271, 2909197, 2944261, 2976487, 3048841, 3090091, + 3116107, 3125281, 3225601, 3363121, 3375041, 3400013, 3413533, 3429037, 3539101, 3542533, + 3567481, 3568661, 3605429, 3656449, 3763801, 3828001, 3898129, 3911197, 3985921, 4072729, + 4181921, 4188889, 4209661, 4360621, 4469471, 4480477, 4513841, 4835209, 4863127, 4869313, + 4877641, 4922413, 5016191, 5044033, 5095177, 5173169, 5173601, 5176153, 5256091, 5271841, + 5284333, 5351537, 5489641, 5590621, 5672041, 5919187, 6027193, 6118141, 6140161, 6159301, + 6189121, 6226193, 6233977, 6236257, 6278533, 6334351, 6368689, 6386993, 6631549, 6658669, + 6779137, 6787327, 6836233, 6952037, 6955541, 6998881, 7017193, 7232321, 7306261, 7306561, + 7429117, 7462001, 7674967, 7725901, 7759937, 7820201, 7883731, 8036033, 8095447, 8239477, + 8384513, 8534233, 8725753, 8727391, 8902741, 9006401, 9056501, 9073513, 9131401, 9345541, + 9371251, 9439201, 9480461, 9533701, 9564169, 9567673, 9588151, 9591661, 9729301, 9774181, + 9863461, 10024561, 10084177, 10323769, 10331141, 10386241, 10425511, 10610063, 10700761, 10712857, + 10763653, 10974881, 11081459, 11115037, 11335501, 11541307, 11585293, 11592397, 11777599, 12032021, + 12096613, 12263131, 12322133, 12327121, 12599233, 12854437, 13057787, 13338371, 13446253, 13500313, + 13635289, 13694761, 13747361, 13773061, 14026897, 14154337, 14179537, 14324473, 14469841, 14671801, + 14676481, 14709241, 14794081, 14796289, 14865121, 15101893, 15139199, 15162941, 15188557, 15220951, + 15247621, 15479777, 15525241, 15603391, 15621409, 15700301, 15802681, 15976747, 15978007, 16070429, + 16132321, 16149169, 16153633, 16324001, 16349477, 16360381, 16705021, 16773121, 16822081, 16843009, + 16853077, 16879501, 16973393, 17098369, 17116837, 17134043, 17208601, 17236801, 17327773, 17375249, + 17405537, 17585969, 17870561, 18067501, 18073817, 18366937, 18443701, 18454921, 18535177, 18653353, + 18740971, 19328653, 19384289, 19404139, 19471033, 19607561, 20261251, 20417311, 20647621, 20968501, + 21042001, 21303343, 21306157, 21359521, 21397381, 21400481, 21623659, 21654533, 22075579, 22087477, + 22369621, 22591301, 22669501, 22711873, 22849481, 22953673, 23247901, 23382529, 23464033, 23577497, + 23634181, 23734901, 23828017, 23872213, 23963869, 24214051, 24356377, 25080101, 25150501, 25276421, + 25326001, 25457833, 25629913, 25696133, 25768261, 25909453, 26280073, 26377921, 26821601, 26840269, + 26877421, 26886817, 27108397, 27118601, 27219697, 27271151, 27279409, 27331921, 27380831, 27392041, + 27409541, 27491237, 27509653, 27664033, 27798461, 27808463, 28325881, 28527049, 28572961, 29111881, + 29214541, 29581501, 30022129, 30090817, 30185569, 30219757, 30295141, 30338593, 30388753, 30418957, + 30576151, 30662497, 30740417, 30881551, 30894307, 31040833, 31166803, 31436123, 31735621, 31759121, + 32091781, 32095057, 32168117, 32285041, 32497921, 32676481, 33146717, 33298337, 33600533, 33627301, + 33704101, 33872593, 34003061, 34043101, 34124641, 34540801, 34856167, 34944001, 35576599, 35703361, + 35820937, 35851037, 36291193, 36307981, 36861901, 36919681, 36974341, 37109467, 37376509, 37439201, + 37964809, 37988497, 38010307, 38046817, 38118763, 38210323, 39465091, 39512773, 39655153, 39684157, + 40165093, 40238797, 40315441, 40361197, 40629601, 40782589, 40827473, 40987201, 41121433, 41568101, + 41604109, 41642681, 41662297, 41840809, 42009217, 42485119, 42623017, 42984589, 43224397, 43363601, + 43661257, 44070841, 44314129, 44465221, 44482901, 45100177, 45175201, 45219329, 45414433, 45819541, + 45879941, 46094401, 46325029, 46386589, 46469809, 46517857, 46679761, 46860001, 47220367, 47903701, + 47918581, 48064021, 48191653, 48269761, 48316969, 48400753, 48448661, 48551161, 48563089, 49075417, + 49303801, 49411801, 49459801, 50155733, 50201089, 50443201, 50523661, 51030601, 51129781, 51302353, + 51500521, 52072021, 52119289, 52204237, 53283169, 53399449, 53656021, 53675623, 53695721, 53711113, + 54029741, 54449431, 55109401, 55176097, 55318957, 55729957, 56052361, 56420033, 56479897, 56810137, + 57762433, 58003213, 58422409, 58449847, 58509977, 58679941, 58755877, 59631211, 59840537, 59913157, + 59953741, 60155201, 60352921, 60547831, 60566431, 60581401, 60696661, 60738257, 60957361, 61201009, + 61219789, 61377109, 61832377, 62756641, 63001801, 63002501, 63065281, 63167743, 63318169, 63328469, + 63346999, 63388033, 64148717, 64605041, 64735897, 65144501, 65254393, 65301013, 65350801, 65359477, + 66096253, 67194401, 67642513, 67928221, 68102641, 68154001, 68165761, 68512867, 68621701, 68839597, + 69030901, 69128641, 69176647, 69228967, 69231061, 69485281, 69612061, 69885649, 70149631, 70463489, + 70593931, 70728121, 71079661, 71734417, 72498253, 72543547, 73562833, 73645001, 74411131, 74927161, + 75140137, 75565873, 76725091, 76745101, 77533123, 77648941, 77812153, 77817979, 78939089, 79398901, + 79411201, 79417801, 79464533, 79786523, 80142761, 80146909, 80375707, 80556337, 80687881, 80891009, + 81433591, 81954133, 82273201, 82506439, 82870517, 82929001, 83083001, 83103329, 83204801, 84164033, + 84350561, 84421081, 84487457, 84998503, 85328717, 85519337, 85823401, 86027329, 86438857, 86530621, + 86999837, 87499651, 87694261, 88256449, 88368853, 88661861, 89308771, 89784581, 90270613, 90278161, + 90341197, 90665789, 90698401, 91433281, 91659283, 92438581, 92625121, 93431521, 93541537, 93571633, + 93643201, 93677761, 93926197, 94316401, 94502701, 95451361, 95452781, 96135601, 96618397, 96791881, + 96888641, 96895441, 96904081, 96925921, 97255801, 97496449, 97796953, 97863529, 97924217, 99036001, + 99115297, 99486889, 99789673, 99898801, 100463443, 100618933, 100943201, 101152133, 101218921, 101270251, + 101276579, 101649241, 102004421, 102678031, 102690677, 102690901, 103301633, 104078857, 104524421, 104988673, + 105305443, 105919633, 106485121, 106622353, 106743073, 107360641, 107543333, 108596953, 109231229, 109437751, + 109541461, 109879837, 110135821, 110139499, 110312773, 110413333, 110717861, 111370141, 111654401, 112032001, + 112402981, 112828801, 113589601, 113605201, 113730481, 113892589, 114305441, 114329881, 114701341, 114842677, + 114910489, 115039081, 115174681, 115497901, 115804501, 115873801, 116090081, 116321617, 116617289, 116682721, + 116696161, 116998669, 117987841, 118466401, 118901521, 119092801, 119204809, 119261113, 119327041, 119558011, + 119743537, 119940853, 120296677, 120517021, 120838609, 121062001, 121374241, 121472359, 121609489, 122166307, + 122396737, 122941981, 123481777, 123671671, 123877081, 123987793, 124145473, 124630273, 124818601, 125284141, + 125686241, 125848577, 126132553, 127050067, 128079409, 128124151, 128396921, 128468957, 128665319, 128987429, + 129205781, 129256273, 129357061, 129461617, 129524669, 130556329, 130693393, 130944133, 131023201, 131567929, + 131938561, 132332201, 132338881, 132440521, 132575071, 133216381, 133302781, 133467517, 133800661, 134696801, + 134767153, 134868029, 135263269, 135296053, 135308881, 135945853, 135969401, 136043641, 136661201, 136722433, + 137415821, 137763037, 138030721, 138403981, 138828821, 139295701, 139487041, 140197051, 142525333, 142922413, + 143106133, 143168581, 145348529, 146156617, 146272901, 146659801, 146843929, 146884393, 147028001, 147287141, + 148109473, 148171769, 148910653, 149389633, 150379693, 150960239, 150988753, 151533377, 151589881, 152716537, + 152922001, 152991841, 153369061, 153589801, 153754873, 153928133, 154287451, 154513633, 154944533, 155203361, + 156114061, 156532799, 157069189, 157368661, 157405249, 157725829, 158068153, 158192317, 158397247, 158496911, + 158544401, 158895281, 160348189, 160378861, 160491329, 160587841, 160672201, 160730389, 161184013, 161216021, + 161289649, 161304001, 161423377, 162026869, 162067441, 162690481, 162771337, 162776041, 163442551, 163954561, + 164111281, 165061909, 165224321, 165938653, 166082309, 166339057, 166406561, 166827943, 167579497, 167582377, + 167692141, 167881121, 168566501, 169655641, 170640961, 170782921, 170856533, 171454321, 172116181, 172436713, + 172947529, 173401621, 174479729, 176030977, 176597821, 176609441, 176977921, 177167233, 177254533, 177693521, + 177927641, 177951973, 178837201, 178956971, 179083601, 179285137, 179820257, 180115489, 180497633, 180703451, + 181285001, 181285537, 181542601, 181647497, 182383111, 183677341, 184411567, 185653333, 186183469, 186393481, + 186983521, 187050529, 187667969, 187761241, 188516329, 188985961, 189714193, 189738361, 189941761, 190212181, + 190382161, 190913297, 191233813, 191648161, 191981609, 192346153, 192857761, 193330237, 193638337, 193949641, + 194556451, 196035001, 196049701, 196231393, 198982759, 199674721, 200143351, 200753281, 201261061, 202130197, + 202156813, 202538857, 203505697, 204280501, 204582457, 204766381, 205057561, 206304961, 206453509, 206504033, + 206529737, 207008569, 207030541, 207132481, 207477001, 207618781, 208051201, 208969223, 209246701, 209404369, + 209990881, 210592873, 210842113, 213035761, 214038533, 214110541, 214852609, 214858717, 215436241, 216821881, + 217123069, 217875571, 218603617, 218642029, 218947121, 219621781, 220531501, 220883521, 221368153, 221415781, + 221884001, 222010721, 222630193, 223449463, 223625851, 223782263, 224074369, 224136013, 224769241, 224957893, + 225853633, 226359547, 226450297, 227132641, 227444101, 227475481, 228652201, 228842209, 228988033, 229589413, + 230357761, 231383461, 231405701, 231927781, 232114433, 232460821, 232771501, 233110081, 234869009, 235426913, + 235928071, 237791143, 238001653, 238833421, 240068041, 240371713, 240694513, 240785047, 241505377, 242067841, + 242650717, 242860069, 243583201, 243955141, 244883981, 245006623, 245950561, 246099317, 246282511, 246434761, + 246658441, 247318957, 247321301, 247416101, 249582481, 250436033, 250958401, 250988173, 251528401, 251663837, + 251855893, 252853921, 253610281, 253893397, 255416897, 256831433, 257590661, 258020473, 258043229, 258234401, + 258944401, 259763093, 259765747, 260156101, 260518801, 260736341, 260963389, 261186001, 261703417, 262979501, + 263428181, 264269449, 264384469, 265020001, 265584133, 265735969, 265836161, 266790481, 266925601, 270525737, + 271272569, 271763467, 271826629, 271950829, 273361789, 273480637, 274701913, 274810241, 274919401, 275283401, + 275619961, 276018913, 276131137, 276542401, 276638321, 277787141, 278943061, 279377281, 280885153, 282253141, + 282471853, 282769771, 283900961, 284166877, 284301751, 284736091, 284834299, 285820501, 286316801, 287160301, + 287449091, 287715121, 288099001, 288117721, 288735277, 290643601, 290706781, 290953921, 291088513, 291461633, + 292153681, 292290181, 292433321, 292902481, 293346637, 293847721, 293938261, 295419097, 295743017, 297624961, + 297798961, 298212601, 299367877, 299736181, 301413001, 302635351, 304080001, 307629401, 307694323, 307972801, + 308483209, 309666361, 310474249, 310978027, 311177213, 311411629, 311655829, 311671361, 312408113, 312614021, + 314184487, 315034513, 315351521, 317137969, 317365933, 317641171, 317796119, 319053281, 319374577, 319440769, + 319726177, 320326003, 321324589, 321850849, 322469701, 322941881, 324477697, 325028089, 325352101, 325546873, + 326266051, 326405713, 326469137, 326628721, 326694301, 326695141, 327073601, 327093409, 327398009, 328302901, + 329153653, 329769721, 330198331, 330759617, 331658081, 331934989, 337135501, 337420679, 337665901, 337783981, + 338125537, 338458807, 338914369, 339195097, 339492169, 339794641, 341958121, 341994131, 343017529, 343052833, + 344201441, 344255551, 344776301, 346080391, 348989101, 349752913, 350031973, 350244577, 351058753, 351177769, + 352802803, 352932337, 353815801, 353932801, 354062809, 356604421, 356836819, 357348601, 357872971, 358416577, + 359394751, 359727073, 360145633, 360375181, 360787771, 361307521, 361312337, 362569201, 363170837, 363430637, + 364550761, 365077373, 365231401, 366487201, 366532321, 366652201, 367559501, 367632301, 368016949, 368476501, + 369667561, 371011801, 371611153, 372167101, 373012777, 373533617, 373669453, 373906513, 374346361, 374988661, + 376957153, 377192353, 377334497, 377458849, 377806687, 377869031, 378792649, 379732501, 380137633, 382304161, + 384100001, 385175113, 385319089, 387072661, 388695301, 390609941, 390612221, 391014937, 392679737, 393611653, + 394723177, 396864469, 399156661, 399302581, 399647221, 400385701, 400557109, 401100881, 403095967, 403293313, + 405739681, 405782623, 407737201, 407889161, 409302001, 409458241, 410613809, 410680357, 411618241, 411851389, + 412836689, 413138881, 413429801, 413778817, 414216461, 414368641, 415200361, 415204501, 415476343, 416964241, + 417767201, 417779909, 418044563, 418226581, 418616161, 418617281, 418667401, 419184481, 420607441, 421942951, + 422429041, 422928101, 423384001, 423465001, 424175761, 424411501, 424431541, 425967301, 426174101, 426219649, + 426770437, 426783811, 427294141, 428180191, 428758201, 429135841, 429509837, 430046857, 430381921, 430646401, + 430733701, 432227449, 434042801, 435016187, 435358657, 435993301, 436465501, 437247841, 437462101, 437597101, + 437866087, 439309261, 441354497, 441650591, 441758461, 442050577, 442181291, 442543553, 444660421, 445429693, + 446414621, 446619617, 449501761, 450807481, 450866021, 450872573, 452990401, 453366029, 453967739, 454745773, + 455198563, 457274161, 457320533, 459785089, 460251733, 460585861, 461151121, 461272267, 461329601, 462587329, + 462639409, 462701513, 464012033, 464955857, 465505633, 466290949, 466758181, 467100937, 468410113, 468950021, + 470120257, 470268137, 470644021, 471535373, 471664513, 472814413, 473581057, 474892741, 474970501, 474983881, + 475723849, 478614067, 479962009, 480668347, 481153501, 481239361, 482488393, 482824669, 482921297, 483006889, + 483029821, 483945601, 484200289, 486063001, 486902929, 487896601, 488104681, 488169289, 488585521, 488656981, + 489994201, 490950461, 491738801, 493108481, 494288677, 495909871, 496109729, 496560349, 497148599, 497285713, + 498662561, 498706651, 498905189, 500747293, 501172241, 501472333, 502686713, 504870241, 505473263, 505532773, + 505798213, 506349421, 507142567, 507323521, 508606771, 509302873, 509551201, 510925609, 511098521, 511215521, + 511611673, 512330281, 514738981, 516045197, 516259657, 516764063, 517662001, 518216201, 518548801, 521501473, + 522390109, 522758233, 523756711, 526067821, 526359289, 526686889, 528013333, 528043753, 528220117, 530630701, + 531095029, 531681281, 532126801, 532758241, 532800133, 533429881, 534782293, 535252867, 535428577, 535517581, + 536003333, 536114197, 536342419, 536870911, 540207097, 540621181, 540654409, 540680141, 542497201, 542536457, + 544861633, 545550433, 545622401, 546102481, 546117301, 546322201, 548080513, 548989561, 549308761, 550132741, + 550230409, 550635373, 550853137, 551313001, 552573793, 553027201, 554487121, 554599051, 554964001, 555321007, + 555465601, 556001377, 556069849, 556095433, 556114609, 557165209, 558235109, 558900821, 558977761, 561448487, + 562367821, 563298061, 563947141, 564298489, 564689381, 565664761, 565707061, 567358513, 567596401, 568902001, + 568967221, 569332177, 569495809, 570941881, 572123521, 572228929, 572430769, 572567353, 572936869, 573817861, + 573862021, 574998841, 575326033, 576724219, 577210181, 577352641, 577613261, 579606301, 579956653, 581618143, + 582389641, 582799951, 585261637, 586706821, 587343541, 588049001, 591242653, 591822001, 592467451, 592468777, + 593682169, 593728489, 595405201, 595590841, 597537361, 597717121, 599135767, 599945293, 600893921, 601606487, + 602379181, 604584221, 605454917, 605961049, 606872449, 607148653, 607750681, 608421637, 608917753, 609361567, + 609813781, 611097401, 611374453, 611770513, 611812321, 611817421, 612006253, 613849601, 614742241, 615361183, + 615760133, 615895897, 616280897, 617087701, 619239457, 619365121, 619480601, 620169409, 620544961, 620755537, + 621769669, 622137601, 623735953, 624303241, 624732421, 625060801, 625482001, 626717471, 627886657, 628868467, + 629134081, 630496621, 630622753, 630811513, 631767943, 631974613, 633289807, 635155291, 635291077, 635319361, + 636287653, 636337073, 636936697, 638502913, 638837761, 639305921, 639807781, 640650931, 640977373, 643036321, + 643316461, 643552909, 644004817, 644453633, 644457551, 644731357, 644900257, 645556481, 648056449, 648328801, + 651011329, 651064681, 651151801, 651514753, 652469641, 653235841, 653260633, 655264369, 657732349, 659526601, + 659846021, 660095641, 660754117, 661122881, 661207177, 662134201, 663760681, 665462081, 668498321, 670976641, + 670987021, 671716921, 672103001, 672108193, 673778827, 675260477, 676359391, 678481693, 680983817, 681019921, + 681124207, 681303241, 682528687, 683316001, 683362681, 684350833, 686059921, 687741401, 689537441, 690035713, + 690562601, 691131349, 692535637, 693456521, 694116893, 696042901, 696321949, 696998251, 697821857, 698192041, + 698819711, 702683101, 705303457, 705351583, 706728377, 707691601, 709409993, 710382401, 710617861, 710721001, + 714490481, 717096641, 717653129, 717831211, 720767521, 722955773, 724160251, 724969087, 725508241, 731276521, + 732805681, 734166217, 736668013, 739444021, 739576801, 740988151, 741182401, 741214237, 742017181, 742550401, + 744500641, 745493761, 745745461, 746331041, 747406801, 748638001, 749172821, 749640161, 750632137, 751226401, + 751705597, 752186593, 753233717, 753574537, 753594001, 754020361, 754874257, 756205633, 756271909, 756980137, + 758581651, 758687581, 758901701, 759252367, 759266621, 759638881, 762699649, 763907741, 764033999, 764240611, + 765378241, 766303693, 766823797, 770201221, 770909107, 770937931, 771043201, 771337891, 772495777, 773131927, + 773807401, 775368901, 775896181, 776443769, 777218989, 781471001, 782823281, 784450393, 784777393, 784783477, + 784966297, 787085857, 787209277, 788046901, 788931361, 789082001, 790453049, 791118043, 792144161, 792145729, + 794201333, 794399041, 794937601, 795064909, 796072003, 796200901, 796560703, 797418997, 797834017, 799162561, + 799630753, 799898833, 799916101, 801093011, 801227269, 801866647, 804978721, 805505957, 805771501, 807115753, + 807218413, 808214161, 809790881, 810023881, 810455101, 811110301, 811478533, 811607777, 811730923, 815430533, + 815796413, 816024161, 816215401, 816549121, 817832329, 818401321, 819466201, 819743233, 822018961, 822531841, + 824389441, 826004467, 829512001, 830664451, 831933901, 832048447, 832127489, 832169857, 833610751, 837766217, + 839268139, 839280691, 839908217, 840749761, 841217653, 841660961, 842785841, 842824981, 842960981, 843161887, + 844545271, 845376533, 846961321, 848090377, 848755969, 849548671, 852432769, 854094781, 854868257, 855734401, + 857100421, 857902861, 858687103, 859096477, 860334301, 862082677, 862678081, 863196181, 863609113, 863984881, + 865242841, 867022747, 867110501, 867638201, 868088341, 868111597, 868691401, 870985223, 871157233, 871195561, + 871908481, 876850801, 877542481, 878492941, 878940833, 879995689, 880870513, 880922657, 883276549, 884304037, + 884952001, 886180429, 887795221, 888868441, 892740853, 893692819, 894264337, 896901461, 897087361, 897283213, + 899019353, 900736411, 901848301, 902566501, 903108821, 903390643, 905040953, 907378669, 907670501, 907711561, + 908005249, 910202509, 910867481, 911484421, 914348737, 914906539, 920375821, 920696653, 921858631, 922845241, + 923437213, 926756881, 927106561, 927877001, 929159941, 930530701, 932148253, 933729421, 935794081, 936421141, + 937675393, 938376181, 939947009, 940123801, 941056273, 941734657, 943271569, 944832533, 946034057, 946787377, + 947878081, 949317217, 949697233, 952893881, 954924013, 957600541, 957631249, 958131157, 958735681, 960269377, + 960946321, 962442001, 962489557, 962523169, 964412837, 965501857, 967266451, 967287751, 967790401, 968283247, + 968413217, 968751241, 969528337, 970586713, 971975071, 974113601, 974471243, 974774401, 975576281, 976396961, + 977483449, 979363153, 980056507, 980725201, 981484561, 983456377, 984133441, 984252001, 985052881, 985075681, + 987842101, 994133479, 995586373, 995650921, 997836841, 998489017, 998590601, 998596741, 998724481, 999828727, + 1002261781, 1003062061, 1005402133, 1005833971, 1006800829, 1008777001, 1008839999, 1009025263, 1009140161, 1011319501, + 1011333061, 1011570457, 1011909271, 1012438391, 1013833153, 1015339441, 1015626151, 1017748057, 1020515761, 1021281301, + 1022336611, 1024041853, 1024123501, 1024605121, 1025035129, 1026738161, 1027744453, 1028494429, 1034252929, 1034958601, + 1040234231, 1049584313, 1050102901, 1050535501, 1054999441, 1055009117, 1056121453, 1057426651, 1063212481, 1065508321, + 1065602281, 1066972301, 1069388497, 1070639389, 1070941987, 1071512749, 1071643249, 1072898711, 1073159281, 1073288581, + 1073484823, 1075100041, 1077133397, 1078467589, 1081798061, 1082472553, 1084241341, 1084444481, 1090858081, 1093150081, + 1093352833, 1093526353, 1094042321, 1097416321, 1098743563, 1100624857, 1101623381, 1101673501, 1102573501, 1102750013, + 1104194521, 1105038871, 1106529761, 1106580817, 1106595493, 1107138961, 1108135381, 1109304913, 1110582947, 1111205873, + 1111939201, 1112671603, 1114277221, 1116379301, 1117202557, 1117785881, 1117828001, 1117890019, 1119412321, 1120076281, + 1120981021, 1121176981, 1123406047, 1123625501, 1123727617, 1124396521, 1125038377, 1127040769, 1130933429, 1134367777, + 1138289041, 1138607233, 1139137057, 1140573601, 1142466151, 1147434289, 1148578201, 1150229761, 1151670001, 1153164097, + 1153440289, 1154343961, 1154691409, 1154987209, 1155939709, 1156761911, 1156993373, 1157839381, 1159421509, 1160844821, + 1163098249, 1163227759, 1164218641, 1165717129, 1166475601, 1166598217, 1168221121, 1168256953, 1168492417, 1173229201, + 1173545533, 1174300093, 1180970407, 1181566219, 1183338241, 1184554801, 1186325981, 1187235193, 1191153937, 1191216133, + 1192314817, 1192412033, 1192903531, 1193229577, 1193557093, 1195524181, 1196852273, 1198650961, 1198880261, 1200456577, + 1200778753, 1202142061, 1204205449, 1205606533, 1205772499, 1209998077, 1210393801, 1210562701, 1210653541, 1213619761, + 1217181061, 1217823517, 1217924159, 1219816261, 1219858921, 1220114377, 1221127013, 1222861271, 1223531677, 1223941657, + 1225128829, 1226230297, 1226855293, 1227220801, 1229491063, 1229751667, 1230446653, 1231362793, 1232445677, 1234125721, + 1234646533, 1235188597, 1235864033, 1236313501, 1236442421, 1238825569, 1242171349, 1242858317, 1249166881, 1249785941, + 1250656621, 1252236421, 1254277909, 1255665613, 1257102001, 1258903981, 1260332137, 1263293281, 1264145401, 1265477791, + 1266003461, 1266273793, 1266425101, 1267345081, 1269295201, 1269835201, 1270193401, 1270489621, 1270667353, 1272558739, + 1272866167, 1282447477, 1282568741, 1285636801, 1286298133, 1286298263, 1296613501, 1297443913, 1299072721, 1299784141, + 1299963601, 1301509249, 1301926081, 1302745481, 1306836001, 1307004641, 1307520469, 1307823661, 1308758533, 1308998741, + 1309723213, 1309983901, 1310329567, 1311255661, 1311616153, 1312332001, 1312573123, 1313396221, 1315858381, 1316169541, + 1318126321, 1318717531, 1319978701, 1319992181, 1320793813, 1321058213, 1323668917, 1325172421, 1325329297, 1328256247, + 1329174601, 1329431689, 1331973329, 1341010577, 1341926401, 1343575381, 1344597577, 1344975721, 1345514101, 1345523401, + 1347387361, 1348964401, 1350685001, 1351126261, 1352453257, 1353051517, 1356241321, 1356328121, 1357459183, 1362463807, + 1362515701, 1362742561, 1365662917, 1366587661, 1366608377, 1368769681, 1371908137, 1372681861, 1375322101, 1376799577, + 1378646179, 1379464633, 1382453333, 1383283129, 1385656829, 1386705433, 1388972353, 1389353941, 1389975149, 1391890033, + 1393851553, 1394640941, 1394746081, 1394942473, 1397357851, 1398883201, 1400859847, 1401840833, 1404008369, 1404253369, + 1406826241, 1406851249, 1409372779, 1413803197, 1414154827, 1414529533, 1415969101, 1417986901, 1421475031, 1424503849, + 1425860101, 1426319563, 1426534201, 1427771089, 1428966001, 1432354901, 1435091377, 1438648993, 1440231941, 1440922891, + 1441139641, 1441678411, 1442945689, 1443388481, 1443742273, 1446298309, 1446434677, 1446818651, 1448921633, 1451635201, + 1454282449, 1454445413, 1456527461, 1457378449, 1461307717, 1463065501, 1463178817, 1463992661, 1464568381, 1465908193, + 1465945417, 1468540477, 1468824787, 1469059481, 1469960377, 1470080501, 1470650851, 1471628401, 1472221921, 1473580001, + 1477289941, 1481626513, 1482274513, 1482876673, 1483873861, 1483918801, 1485061471, 1486564301, 1493114149, 1495190699, + 1497221281, 1497965713, 1499971457, 1499989177, 1500142001, 1501165097, 1502171117, 1502403121, 1503240559, 1503705601, + 1504139521, 1504832033, 1507746241, 1509156013, 1510870241, 1511558533, 1515175087, 1515785041, 1517039371, 1518014689, + 1518290707, 1520190341, 1521221473, 1522302121, 1526732803, 1529648231, 1529819971, 1530495289, 1532419099, 1532569681, + 1532755369, 1533343261, 1534063081, 1535020133, 1536112001, 1536251047, 1536883357, 1537433899, 1537641691, 1538012449, + 1539583921, 1539804001, 1540454761, 1540550413, 1541047813, 1541849761, 1541955409, 1544145121, 1545019813, 1545177581, + 1546106773, 1546340401, 1546508057, 1547140841, 1547543161, 1547712601, 1550924873, 1554270481, 1557118081, 1560312001, + 1560620041, 1561800833, 1565893201, 1566594551, 1567830241, 1568916311, 1574362441, 1574601601, 1577983489, 1578009401, + 1580449201, 1581576641, 1581714481, 1582783777, 1583230241, 1583658649, 1586436193, 1587650401, 1590394313, 1593706201, + 1595647351, 1595887921, 1598197201, 1602517949, 1603765021, 1603810561, 1603994701, 1609916491, 1609935913, 1612121473, + 1614508267, 1617795181, 1617921667, 1619447741, 1620646177, 1627103521, 1627898401, 1628692201, 1630062253, 1630307617, + 1631314609, 1632286673, 1632513601, 1633044241, 1636185601, 1637434657, 1637436457, 1637930893, 1638294661, 1639351981, + 1639846391, 1641971701, 1642814653, 1644637051, 1645413001, 1647225529, 1648076041, 1649430889, 1650265549, 1650682153, + 1654940509, 1655660761, 1656229921, 1656280033, 1656917377, 1659009601, 1661202113, 1668037621, 1668926629, 1669893661, + 1671603667, 1671714241, 1672125131, 1674091141, 1674658133, 1675978193, 1678274581, 1679130641, 1680901381, 1683174533, + 1685433413, 1686001861, 1687248001, 1691745821, 1692605041, 1694128129, 1695158921, 1696893101, 1698707377, 1699279441, + 1700250049, 1709909293, 1710753001, 1712392321, 1714322377, 1716160321, 1716714793, 1716774481, 1718013133, 1718088301, + 1719197621, 1721061497, 1721986313, 1722007169, 1722685777, 1725675451, 1726372441, 1731048937, 1731995497, 1732924001, + 1734059291, 1734285601, 1735071913, 1736481601, 1738687469, 1740214841, 1742288881, 1742815621, 1743166441, 1744605097, + 1746692641, 1746721681, 1749124829, 1750412161, 1754818561, 1757148121, 1760014561, 1766984389, 1767234613, 1769091241, + 1769267761, 1770236893, 1771303801, 1772267281, 1773582977, 1776439261, 1776820033, 1779649381, 1779892577, 1784306273, + 1784638309, 1785843547, 1786005521, 1787934881, 1790023861, 1791426787, 1792442737, 1792588813, 1794814103, 1801558201, + 1801774081, 1802510669, 1803768091, 1804906517, 1805947313, 1809888967, 1816408273, 1817067169, 1819829749, 1820306953, + 1821514633, 1828682101, 1828887061, 1831258601, 1835114401, 1837156049, 1837599769, 1839568981, 1841034961, 1841099261, + 1841479501, 1844028961, 1846171781, 1847811673, 1849964117, 1850233897, 1850598961, 1852496761, 1853926777, 1854084649, + 1854940231, 1856689453, 1857221281, 1858098497, 1858197961, 1860373241, 1861026133, 1861880689, 1862880401, 1866409861, + 1867906721, 1868682241, 1871987041, 1872937057, 1873177693, 1874634721, 1874849929, 1878691753, 1879111697, 1879623157, + 1879775501, 1883509633, 1883785681, 1885915841, 1894909141, 1894955311, 1897700113, 1899081757, 1899525601, 1900687381, + 1903447841, 1904658913, 1905958891, 1908088001, 1909566073, 1910134309, 1911197947, 1912950241, 1914303841, 1915391521, + 1916987593, 1917397637, 1920301951, 1921309633, 1922092567, 1922687293, 1923224689, 1923311317, 1923845801, 1924201501, + 1925042737, 1928903971, 1929862849, 1930403333, 1930447501, 1930534453, 1930915169, 1934350351, 1938264241, 1940048881, + 1943951041, 1944125633, 1945042181, 1950987193, 1952513369, 1952968753, 1957705177, 1959659857, 1960708261, 1963149553, + 1965007601, 1968002149, 1970065681, 1974474049, 1977257441, 1982123893, 1982826961, 1988071801, 1988713189, 1988835713, + 1988965861, 1989192277, 1991063449, 1995784961, 1995830761, 1996231189, 1996339649, 1997844157, 1998780001, 1999053601, + 1999111801, 1999743661, 2004299641, 2007646961, 2013554869, 2013834961, 2016481477, 2017021333, 2017509601, 2019564769, + 2021392369, 2021884343, 2027675701, 2028279793, 2028631361, 2028812399, 2029830409, 2030600833, 2036224321, 2043173273, + 2049293401, 2050617713, 2052149221, 2054711381, 2055634561, 2057267941, 2057835781, 2058072041, 2059739221, 2062612033, + 2068867841, 2070739441, 2072624761, 2076192007, 2081039297, 2081551753, 2082146617, 2083034113, 2083997441, 2085453649, + 2085882661, 2086645009, 2093300401, 2095627153, 2096046457, 2097317377, 2100292841, 2101470541, 2101744837, 2104994449, + 2106147457, 2107148761, 2114643217, 2115769633, 2115986557, 2116483027, 2116541221, 2117031263, 2117555641, 2118621097, + 2120096161, 2123601751, 2124078653, 2124691213, 2127197489, 2128104001, 2129304997, 2130134533, 2131004737, 2131811501, + 2140699681, 2140771609, 2141340833, 2144961253, 2147418113, 2147429509, 2152627801, 2154446641, 2155416251, 2156151313, + 2164282177, 2168431201, 2170282969, 2172155819, 2173499329, 2173540951, 2173579801, 2175126601, 2175406201, 2175646177, + 2177374321, 2177645557, 2178082901, 2178939221, 2180221201, 2182281601, 2182802689, 2185362233, 2187717761, 2193980881, + 2199617701, 2200115713, 2201924341, 2202101761, 2202205897, 2203649197, 2203856497, 2206095589, 2210578759, 2213431729, + 2216960929, 2217879901, 2219072017, 2224252801, 2229468697, 2231332357, 2233031701, 2240507821, 2241880033, 2241982009, + 2244932281, 2245519981, 2246762899, 2248354153, 2251732033, 2254314241, 2254757077, 2256197761, 2256748777, 2256751837, + 2262861901, 2269307587, 2274584089, 2283289681, 2284416181, 2289251669, 2289624793, 2290316377, 2290910257, 2291205461, + 2292068143, 2295209281, 2296995121, 2299190401, 2300628601, 2300795353, 2301745249, 2304120001, 2308966661, 2309241601, + 2309405617, 2311558021, 2311575001, 2315137261, 2320527613, 2323147201, 2324867399, 2329584217, 2330569541, 2331181621, + 2335341601, 2338157597, 2338728001, 2340460487, 2345907961, 2347597981, 2352371251, 2354453561, 2355230749, 2355320101, + 2355622721, 2355649921, 2355735089, 2358534361, 2360261989, 2370771181, 2370928337, 2371350101, 2372976563, 2374232977, + 2375415841, 2377166401, 2378309041, 2381782597, 2382678101, 2383164577, 2385574201, 2389072321, 2389544977, 2393708761, + 2394311233, 2398393661, 2404912501, 2411128441, 2412172153, 2412675721, 2413973071, 2422296241, 2423401681, 2425249601, + 2428648967, 2428870753, 2428986913, 2429407961, 2430697513, 2431136401, 2431144801, 2432761633, 2432860273, 2433791593, + 2434964321, 2434974433, 2435091221, 2436691321, 2437907779, 2438778413, 2442050353, 2442454561, 2443708961, 2444950561, + 2448039497, 2448374689, 2453473049, 2454285751, 2456536681, 2457846161, 2463713281, 2471205361, 2473120961, 2473189441, + 2473823353, 2474308069, 2474676949, 2476283239, 2477814193, 2478643907, 2480147521, 2480343553, 2482435981, 2482682131, + 2484408301, 2486017249, 2488420801, 2488591117, 2492480233, 2494660033, 2494984321, 2495834329, 2499327041, 2501012599, + 2501771329, 2502525637, 2504008609, 2506529257, 2506733189, 2507121037, 2508178843, 2513230891, 2516684801, 2519297089, + 2525070241, 2526566041, 2528291341, 2529410281, 2529827821, 2529854713, 2530351561, 2532630787, 2533465661, 2533797017, + 2535516173, 2537105761, 2539406281, 2539736257, 2540469901, 2541660367, 2542479481, 2544590161, 2545934077, 2548051801, + 2550139253, 2550780277, 2551365769, 2552418761, 2553272929, 2555391481, 2561945401, 2564536201, 2565186137, 2570087521, + 2571180247, 2575060949, 2575737361, 2577345541, 2582092189, 2582246701, 2582952769, 2583322381, 2584460701, 2588054401, + 2588582089, 2590663681, 2593065721, 2595276353, 2597289241, 2597294701, 2598933481, 2600611861, 2602343521, 2602378721, + 2604465013, 2604803701, 2611122229, 2611461529, 2613382201, 2614688801, 2616180821, 2617563031, 2621080741, 2621977627, + 2622993661, 2624549929, 2625903601, 2626783921, 2627284987, 2630643401, 2632605049, 2634284801, 2634804481, 2634820813, + 2638067881, 2639099233, 2642159809, 2642582251, 2646751249, 2646790033, 2648662777, 2649907201, 2650820329, 2651507713, + 2654716321, 2656494271, 2658630913, 2658696301, 2659265701, 2668095181, 2668469431, 2670972949, 2672605657, 2672651521, + 2676053333, 2677147201, 2677821121, 2678785621, 2681041843, 2682823681, 2683742491, 2684284441, 2687655169, 2688124001, + 2689427281, 2690408533, 2690867401, 2693739751, 2695115473, 2700818017, 2700891839, 2701878941, 2704957909, 2706863833, + 2707661501, 2716157989, 2716275007, 2717428033, 2719319513, 2721666817, 2721721939, 2723859001, 2725357249, 2733156029, + 2736316301, 2738184697, 2740336561, 2744329909, 2746021741, 2753333227, 2753538001, 2759392633, 2765323397, 2766006253, + 2767672189, 2769080161, 2769602333, 2774295577, 2777887297, 2778304273, 2779477741, 2781117721, 2781226477, 2786028337, + 2787998641, 2789218909, 2800352011, 2805762961, 2809635901, 2812672981, 2814748201, 2823570433, 2824256377, 2824804693, + 2824854913, 2828205397, 2832384133, 2832743713, 2837697773, 2837917633, 2840634109, 2840871041, 2841190381, 2847894377, + 2848466281, 2848722131, 2855046421, 2855071801, 2855512909, 2862066481, 2865483601, 2866005139, 2866527841, 2870377309, + 2871536561, 2872527733, 2872948321, 2874382853, 2877769501, 2881429741, 2882370481, 2885594497, 2887955533, 2890316801, + 2890414873, 2892426029, 2894667781, 2895004927, 2899294889, 2903776129, 2915953633, 2916247819, 2918295451, 2920691161, + 2923042141, 2924158001, 2929062533, 2929106753, 2930831641, 2931708097, 2932327549, 2936227603, 2936958181, 2941174897, + 2941343633, 2944555681, 2944677961, 2945208001, 2945549881, 2951136343, 2956724317, 2957320351, 2965700233, 2967053953, + 2968206601, 2974506841, 2975377429, 2976930001, 2978766341, 2980689601, 2986025677, 2987414977, 2990152901, 2993462713, + 2993495041, 2994098281, 2994415201, 2998202353, 2998919873, 3000688381, 3001561441, 3002647829, 3004443679, 3009628301, + 3011421841, 3014101261, 3015502181, 3016957381, 3017444761, 3018147217, 3018576689, 3019916461, 3025350343, 3026575553, + 3028586471, 3030393901, 3033332641, 3034402681, 3034817209, 3035375047, 3036079729, 3037295801, 3037781251, 3038880473, + 3039681457, 3041984353, 3042630533, 3048159841, 3050190163, 3056100623, 3056160929, 3057886591, 3058670677, 3059397793, + 3063685633, 3065998717, 3076505209, 3077122133, 3079496551, 3082054697, 3082068013, 3083053387, 3083537689, 3083884651, + 3088408429, 3089013313, 3091019777, 3094763851, 3099670657, 3103800701, 3112974481, 3114125071, 3115667521, 3120445697, + 3122287981, 3129914881, 3133899409, 3135040133, 3143282221, 3145410761, 3150972917, 3156599161, 3156643141, 3157579861, + 3163106953, 3166504273, 3167442721, 3170262409, 3172658653, 3175204531, 3175255717, 3178375201, 3181356263, 3181391641, + 3182606857, 3182655361, 3182891401, 3185472001, 3187035113, 3187421077, 3187939921, 3196397821, 3196431829, 3197565001, + 3197632441, 3197911001, 3197911741, 3199164901, 3205663921, 3207297773, 3208902491, 3212465437, 3215031751, 3217412881, + 3219808411, 3221580281, 3222693421, 3224143441, 3225081473, 3227082823, 3227209057, 3229131137, 3233558021, 3237992101, + 3242533897, 3248236309, 3250348417, 3250700737, 3252148621, 3257334541, 3258647809, 3258892801, 3261114601, 3263097641, + 3263568901, 3263626957, 3264820001, 3265122451, 3267417677, 3268506541, 3268841941, 3270933121, 3271999249, 3272030401, + 3272702497, 3274264033, 3275671969, 3276075709, 3277047649, 3278640289, 3280067129, 3282974857, 3287174129, 3288757249, + 3295362727, 3296403601, 3299246833, 3302322241, 3304307341, 3305829073, 3306686659, 3306957593, 3310858777, 3312489577, + 3312536569, 3313196881, 3315139717, 3320669437, 3323308501, 3323590463, 3323829169, 3328354801, 3332800021, 3334350781, + 3340214413, 3342005633, 3344191241, 3346172189, 3347908801, 3349218881, 3350993969, 3352091557, 3355382857, 3355953001, + 3357417181, 3359737921, 3360511981, 3369139201, 3371024521, 3371452921, 3371693063, 3372667121, 3373086601, 3381052177, + 3381901921, 3385842877, 3386603221, 3387014401, 3387487351, 3389030261, 3395091311, 3399205591, 3399890413, 3402234749, + 3407609221, 3407772817, 3407952169, 3408135121, 3409339393, 3411250081, 3411574801, 3411829693, 3412575097, 3415379701, + 3415832137, 3417522841, 3420143941, 3421845001, 3423222757, 3423580481, 3427050673, 3428133103, 3429457921, 3429982081, + 3430804297, 3432695921, 3432997537, 3433458073, 3434575327, 3435973837, 3440195713, 3443704261, 3449768513, 3450717901, + 3453900913, 3458257741, 3461861761, 3463907761, 3464236901, 3466158361, 3470716657, 3474335437, 3480174001, 3482161261, + 3485747521, 3489958697, 3491763493, 3492178873, 3492883081, 3493262761, 3497607433, 3504132113, 3512030497, 3512291021, + 3512369857, 3513604657, 3516565057, 3519318721, 3524086333, 3525088961, 3529119361, 3529864391, 3532687201, 3533662129, + 3533856913, 3538213381, 3542303047, 3543203333, 3548378341, 3549286001, 3549988261, 3552158521, 3553567057, 3557646401, + 3562963973, 3563340457, 3566428301, 3574891757, 3582711841, 3583249921, 3583604161, 3584800801, 3586833253, 3587553971, + 3589937261, 3590409439, 3593276353, 3594110081, 3596491907, 3596815169, 3598772761, 3602006101, 3605151241, 3611571121, + 3612298321, 3612825221, 3614770573, 3616574081, 3620631169, 3628526287, 3630596257, 3631828481, 3632452741, 3635993089, + 3649116277, 3649965281, 3650158849, 3651572609, 3656355841, 3658730893, 3662387977, 3662503093, 3663084541, 3668926801, + 3669587533, 3672754633, 3677180797, 3679657997, 3682471321, 3685647701, 3685775741, 3692307161, 3695628133, 3697278427, + 3700801861, 3705582073, 3705623281, 3708123301, 3708905341, 3709626961, 3712887289, 3713287801, 3713448769, 3718226401, + 3721486081, 3723410161, 3723699373, 3725016749, 3727828501, 3729097633, 3733761739, 3736293461, 3745192001, 3746101189, + 3749383681, 3751554581, 3751782737, 3754680403, 3756668401, 3759781369, 3760622689, 3760896133, 3762110881, 3767640601, + 3773061337, 3774337201, 3784123501, 3787491457, 3798040471, 3798626833, 3799111681, 3800084401, 3805699501, 3807112123, + 3807308269, 3807749821, 3809018947, 3813919453, 3817561777, 3817706621, 3821233121, 3827035237, 3832807681, 3833208961, + 3842941741, 3846174151, 3846532801, 3847106803, 3850058689, 3852800033, 3863326897, 3865604023, 3867183937, 3874471147, + 3874523017, 3875096893, 3875965417, 3886515361, 3886643801, 3887423437, 3887635753, 3891892421, 3891919417, 3894053311, + 3896079281, 3897241129, 3897869201, 3898906129, 3900327241, 3903711841, 3905533721, 3905876501, 3907577521, 3907752241, + 3912174421, 3914880337, 3914923211, 3915467341, 3915604421, 3915921241, 3918227437, 3922321561, 3926912669, 3929293061, + 3934940833, 3935864017, 3936123601, 3945165841, 3947233201, 3947383201, 3953408801, 3953949421, 3955572001, 3958597301, + 3958930441, 3959578801, 3960728641, 3962037061, 3966350203, 3967343161, 3971095301, 3973556837, 3979485931, 3982017601, + 3987528793, 3987960913, 3991124341, 3992697997, 3997536427, 4005660961, 4007365741, 4011996871, 4015548769, 4017684529, + 4018283501, 4020144133, 4026822577, 4027012021, 4027518961, 4028465873, 4028771849, 4031223841, 4034969401, 4034993269, + 4035498409, 4036395581, 4042538497, 4044601751, 4044884689, 4048493983, 4053267217, 4054039841, 4057195309, 4058433931, + 4059776533, 4060942381, 4061009971, 4064633821, 4067039461, 4067887501, 4068671881, 4071644893, 4075241633, 4075721921, + 4076009857, 4079665633, 4079682361, 4083376067, 4085074909, 4088147617, 4088838913, 4092929149, 4098258707, 4099180801, + 4100934241, 4103745689, 4105691393, 4108970251, 4109461709, 4109711581, 4110320663, 4113013141, 4115891893, 4117058221, + 4117447441, 4121286907, 4127050621, 4129914673, 4133928761, 4135847101, 4136916001, 4137262541, 4138838401, 4139015987, + 4150174393, 4155375349, 4157008813, 4162880401, 4166032873, 4183664101, 4185636781, 4186561633, 4187360341, 4191864013, + 4192060699, 4195843037, 4196323561, 4204344601, 4206006229, 4206295433, 4212105409, 4215885697, 4218900001, 4220122321, + 4232966251, 4234224601, 4237212061, 4243744201, 4244022301, 4244663651, 4247990917, 4250920459, 4251904273, 4255695013, + 4257003353, 4261352869, 4271267333, 4275011401, 4277526901, 4278305651, 4282867213, 4285148981, 4293088801, 4294901761, + } + + primes16 = [65536]byte{ + 2, 1, 1, 2, 1, 2, 1, 4, 3, 2, // 0-9 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 10-19 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 20-29 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 30-39 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 40-49 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 50-59 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 60-69 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 70-79 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 80-89 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 90-99 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 100-109 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 110-119 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 120-129 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 130-139 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 140-149 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 150-159 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 160-169 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 170-179 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 180-189 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 190-199 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 200-209 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 210-219 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 220-229 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 230-239 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 240-249 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 250-259 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 260-269 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 270-279 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 280-289 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 290-299 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 300-309 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 310-319 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 320-329 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 330-339 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 340-349 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 350-359 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 360-369 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 370-379 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 380-389 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 390-399 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 400-409 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 410-419 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 420-429 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 430-439 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 440-449 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 450-459 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 460-469 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 470-479 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 480-489 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 490-499 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 500-509 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 510-519 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 520-529 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 530-539 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 540-549 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 550-559 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 560-569 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 570-579 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 580-589 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 590-599 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 600-609 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 610-619 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 620-629 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 630-639 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 640-649 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 650-659 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 660-669 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 670-679 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 680-689 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 690-699 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 700-709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 710-719 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 720-729 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 730-739 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 740-749 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 750-759 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 760-769 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 770-779 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 780-789 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 790-799 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 800-809 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 810-819 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 820-829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 830-839 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 840-849 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 850-859 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 860-869 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 870-879 + 1, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 880-889 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 890-899 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 900-909 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 910-919 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 920-929 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 930-939 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 940-949 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 950-959 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 960-969 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 970-979 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 980-989 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 990-999 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 1000-1009 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 1010-1019 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1020-1029 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 1030-1039 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 1040-1049 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1050-1059 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 1060-1069 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 1070-1079 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 1080-1089 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 1090-1099 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 1100-1109 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 1110-1119 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 1120-1129 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 1130-1139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1140-1149 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 1150-1159 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 1160-1169 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1170-1179 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 1180-1189 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 1190-1199 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 1200-1209 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 1210-1219 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 1220-1229 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 1230-1239 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 1240-1249 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 1250-1259 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 1260-1269 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 1270-1279 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 1280-1289 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 1290-1299 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 1300-1309 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 1310-1319 + 1, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 1320-1329 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 1330-1339 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 1340-1349 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1350-1359 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 1360-1369 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 1370-1379 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 1380-1389 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 1390-1399 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 1400-1409 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 1410-1419 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 1420-1429 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 1430-1439 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 1440-1449 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 1450-1459 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1460-1469 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1470-1479 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 1480-1489 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 1490-1499 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1500-1509 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 1510-1519 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 1520-1529 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 1530-1539 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 1540-1549 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 1550-1559 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 1560-1569 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 1570-1579 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 1580-1589 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 1590-1599 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 1600-1609 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 1610-1619 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 1620-1629 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 1630-1639 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 1640-1649 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 1650-1659 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 24, // 1660-1669 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 1670-1679 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 1680-1689 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 1690-1699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 1700-1709 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1710-1719 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 1720-1729 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 1730-1739 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 1740-1749 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 1750-1759 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 1760-1769 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 1770-1779 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 1780-1789 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1790-1799 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1800-1809 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 1810-1819 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 1820-1829 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 1830-1839 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 1840-1849 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1850-1859 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 1860-1869 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 1870-1879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 1880-1889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1890-1899 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 1900-1909 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 1910-1919 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 1920-1929 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 1930-1939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 1940-1949 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 1950-1959 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 1960-1969 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 1970-1979 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 1980-1989 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 1990-1999 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 2000-2009 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 2010-2019 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 2020-2029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 2030-2039 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 2040-2049 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 2050-2059 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 2060-2069 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 2070-2079 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 2080-2089 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 2090-2099 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 2100-2109 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 2110-2119 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 2120-2129 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 2130-2139 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 2140-2149 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 2150-2159 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 2160-2169 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 2170-2179 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 2180-2189 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 2190-2199 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 2200-2209 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 2210-2219 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 2220-2229 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 2230-2239 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 2240-2249 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 2250-2259 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 2260-2269 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 2270-2279 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 2280-2289 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 2290-2299 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 2300-2309 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 2310-2319 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 2320-2329 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 2330-2339 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 2340-2349 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 2350-2359 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 2360-2369 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 2370-2379 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 2380-2389 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 2390-2399 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 2400-2409 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 2410-2419 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 2420-2429 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 2430-2439 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 2440-2449 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 2450-2459 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 2460-2469 + 3, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 2470-2479 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 2480-2489 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 2490-2499 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 2500-2509 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 2510-2519 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 2520-2529 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 2530-2539 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 2540-2549 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 2550-2559 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 2560-2569 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 2570-2579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 2580-2589 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 2590-2599 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 2600-2609 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 2610-2619 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 2620-2629 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 2630-2639 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 2640-2649 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 2650-2659 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 2660-2669 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 2670-2679 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 2680-2689 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 2690-2699 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 2700-2709 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 2710-2719 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 2720-2729 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 2730-2739 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 2740-2749 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 2750-2759 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 2760-2769 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 2770-2779 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 2780-2789 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 2790-2799 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 2800-2809 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 2810-2819 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 2820-2829 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 2830-2839 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 2840-2849 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 2850-2859 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 2860-2869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 2870-2879 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 2880-2889 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 2890-2899 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 2900-2909 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 2910-2919 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 2920-2929 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 2930-2939 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 2940-2949 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 2950-2959 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 2960-2969 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 2970-2979 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 2980-2989 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 2990-2999 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3000-3009 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 3010-3019 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 3020-3029 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 3030-3039 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 3040-3049 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3050-3059 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 3060-3069 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 3070-3079 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 20, // 3080-3089 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 3090-3099 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 3100-3109 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 3110-3119 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 3120-3129 + 7, 6, 5, 4, 3, 2, 1, 26, 25, 24, // 3130-3139 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 3140-3149 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 3150-3159 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 3160-3169 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3170-3179 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 3180-3189 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 3190-3199 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 3200-3209 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 3210-3219 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 3220-3229 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 3230-3239 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3240-3249 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 3250-3259 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3260-3269 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 3270-3279 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 3280-3289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 3290-3299 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 3300-3309 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 3310-3319 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 3320-3329 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 3330-3339 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 3340-3349 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 3350-3359 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3360-3369 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 3370-3379 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 3380-3389 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 3390-3399 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 3400-3409 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 3410-3419 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 3420-3429 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 3430-3439 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 3440-3449 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 3450-3459 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 22, // 3460-3469 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 3470-3479 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3480-3489 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 3490-3499 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3500-3509 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 3510-3519 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 3520-3529 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 3530-3539 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 3540-3549 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 3550-3559 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3560-3569 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3570-3579 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 3580-3589 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 3590-3599 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 3600-3609 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 3610-3619 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 3620-3629 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 3630-3639 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 3640-3649 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 3650-3659 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3660-3669 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 3670-3679 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3680-3689 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 3690-3699 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 3700-3709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 3710-3719 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 3720-3729 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 3730-3739 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 3740-3749 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3750-3759 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 3760-3769 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 3770-3779 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 3780-3789 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 3790-3799 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 3800-3809 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3810-3819 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 3820-3829 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 3830-3839 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 3840-3849 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 3850-3859 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 3860-3869 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 3870-3879 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 3880-3889 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 3890-3899 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 3900-3909 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 3910-3919 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 3920-3929 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 3930-3939 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 3940-3949 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 3950-3959 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 3960-3969 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 3970-3979 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 3980-3989 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 3990-3999 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 4000-4009 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 4010-4019 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 4020-4029 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 4030-4039 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 4040-4049 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 4050-4059 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 4060-4069 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 4070-4079 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4080-4089 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 4090-4099 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4100-4109 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 4110-4119 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 4120-4129 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 4130-4139 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 4140-4149 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 18, // 4150-4159 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 4160-4169 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 4170-4179 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 4180-4189 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4190-4199 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4200-4209 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 4210-4219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 4220-4229 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4230-4239 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 4240-4249 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 4250-4259 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4260-4269 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 4270-4279 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 4280-4289 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 4290-4299 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 4300-4309 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 4310-4319 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 4320-4329 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 4330-4339 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 4340-4349 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 4350-4359 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 4360-4369 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 4370-4379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4380-4389 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 4390-4399 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 4400-4409 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4410-4419 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 4420-4429 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4430-4439 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 4440-4449 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 4450-4459 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 4460-4469 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4470-4479 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 4480-4489 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 4490-4499 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 4500-4509 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 4510-4519 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 4520-4529 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 4530-4539 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 4540-4549 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4550-4559 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 4560-4569 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 4570-4579 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 4580-4589 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 4590-4599 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 4600-4609 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4610-4619 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 4620-4629 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 4630-4639 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 4640-4649 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 4650-4659 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 4660-4669 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 4670-4679 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4680-4689 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 4690-4699 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 4700-4709 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4710-4719 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 4720-4729 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 4730-4739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4740-4749 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 4750-4759 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 4760-4769 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 4770-4779 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 4780-4789 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 4790-4799 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 4800-4809 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 4810-4819 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4820-4829 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 4830-4839 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 4840-4849 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4850-4859 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4860-4869 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 4870-4879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 4880-4889 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 4890-4899 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 4900-4909 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 4910-4919 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 4920-4929 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 4930-4939 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 4940-4949 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 4950-4959 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 4960-4969 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 4970-4979 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 4980-4989 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 4990-4999 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 5000-5009 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5010-5019 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 5020-5029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 5030-5039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5040-5049 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 5050-5059 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 5060-5069 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 5070-5079 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 5080-5089 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 5090-5099 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 5100-5109 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 28, // 5110-5119 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 5120-5129 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 5130-5139 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 5140-5149 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 5150-5159 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 5160-5169 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 5170-5179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 5180-5189 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 5190-5199 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 5200-5209 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 5210-5219 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 5220-5229 + 1, 2, 1, 4, 3, 2, 1, 24, 23, 22, // 5230-5239 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 5240-5249 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5250-5259 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 5260-5269 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 5270-5279 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 5280-5289 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 5290-5299 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 5300-5309 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 5310-5319 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 5320-5329 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 5330-5339 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 5340-5349 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 5350-5359 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 5360-5369 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5370-5379 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 5380-5389 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 5390-5399 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 5400-5409 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 5410-5419 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5420-5429 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 5430-5439 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 5440-5449 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 5450-5459 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5460-5469 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 5470-5479 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 5480-5489 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5490-5499 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 5500-5509 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 5510-5519 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 5520-5529 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 5530-5539 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 5540-5549 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 5550-5559 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 5560-5569 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 5570-5579 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5580-5589 + 1, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 5590-5599 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 5600-5609 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 5610-5619 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 5620-5629 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 5630-5639 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 5640-5649 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 5650-5659 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 5660-5669 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 5670-5679 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 5680-5689 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 5690-5699 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5700-5709 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 5710-5719 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 5720-5729 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 5730-5739 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 30, // 5740-5749 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 5750-5759 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 5760-5769 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 5770-5779 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 5780-5789 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5790-5799 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 5800-5809 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 5810-5819 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 5820-5829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 5830-5839 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 5840-5849 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 5850-5859 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 5860-5869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 5870-5879 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 5880-5889 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 5890-5899 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 5900-5909 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 5910-5919 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 5920-5929 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 5930-5939 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 5940-5949 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 5950-5959 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 5960-5969 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 5970-5979 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 5980-5989 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 5990-5999 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 6000-6009 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 6010-6019 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 6020-6029 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 6030-6039 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 6040-6049 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 6050-6059 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 6060-6069 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 6070-6079 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 6080-6089 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 6090-6099 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 6100-6109 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 6110-6119 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 6120-6129 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 6130-6139 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 6140-6149 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 6150-6159 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 6160-6169 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 6170-6179 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 6180-6189 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 6190-6199 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 6200-6209 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 6210-6219 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 6220-6229 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 6230-6239 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 6240-6249 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 6250-6259 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 6260-6269 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 6270-6279 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 6280-6289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 6290-6299 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 6300-6309 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 6310-6319 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 6320-6329 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 6330-6339 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 6340-6349 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 6350-6359 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 6360-6369 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 6370-6379 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 6380-6389 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 6390-6399 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 6400-6409 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 6410-6419 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 6420-6429 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 6430-6439 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 6440-6449 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 6450-6459 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 6460-6469 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 6470-6479 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 6480-6489 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 6490-6499 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 6500-6509 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 6510-6519 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 6520-6529 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 6530-6539 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 6540-6549 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 6550-6559 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 6560-6569 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 6570-6579 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 6580-6589 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 6590-6599 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 6600-6609 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 6610-6619 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 6620-6629 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 6630-6639 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 6640-6649 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 6650-6659 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 6660-6669 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 6670-6679 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 6680-6689 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 6690-6699 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 6700-6709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 6710-6719 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 6720-6729 + 3, 2, 1, 4, 3, 2, 1, 24, 23, 22, // 6730-6739 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 6740-6749 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 6750-6759 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 6760-6769 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 6770-6779 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 6780-6789 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 6790-6799 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 6800-6809 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 6810-6819 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 6820-6829 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 6830-6839 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 6840-6849 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 6850-6859 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 6860-6869 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 6870-6879 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 6880-6889 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 6890-6899 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 6900-6909 + 1, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 6910-6919 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 6920-6929 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 6930-6939 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 6940-6949 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 6950-6959 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 6960-6969 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 6970-6979 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 6980-6989 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 6990-6999 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7000-7009 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 7010-7019 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 7020-7029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 7030-7039 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 7040-7049 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 7050-7059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 7060-7069 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 7070-7079 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 7080-7089 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7090-7099 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 7100-7109 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7110-7119 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 7120-7129 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 7130-7139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7140-7149 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 7150-7159 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 7160-7169 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 7170-7179 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 7180-7189 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 7190-7199 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 7200-7209 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 7210-7219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 7220-7229 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 7230-7239 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 7240-7249 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 7250-7259 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 7260-7269 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7270-7279 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 7280-7289 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 7290-7299 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 7300-7309 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7310-7319 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7320-7329 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 7330-7339 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 7340-7349 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 7350-7359 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 7360-7369 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 7370-7379 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7380-7389 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 7390-7399 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7400-7409 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 7410-7419 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7420-7429 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 7430-7439 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7440-7449 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 7450-7459 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 7460-7469 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 7470-7479 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 7480-7489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 7490-7499 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 7500-7509 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 7510-7519 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 7520-7529 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 7530-7539 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 7540-7549 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 7550-7559 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7560-7569 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 7570-7579 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 7580-7589 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7590-7599 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 7600-7609 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7610-7619 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 7620-7629 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 7630-7639 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 20, // 7640-7649 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 7650-7659 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 7660-7669 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 7670-7679 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 7680-7689 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 7690-7699 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 7700-7709 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 7710-7719 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 7720-7729 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7730-7739 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7740-7749 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 30, // 7750-7759 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 7760-7769 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 7770-7779 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 7780-7789 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 7790-7799 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 7800-7809 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 7810-7819 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 7820-7829 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7830-7839 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7840-7849 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 7850-7859 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 7860-7869 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 7870-7879 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 7880-7889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 7890-7899 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 7900-7909 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 7910-7919 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 7920-7929 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 7930-7939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 7940-7949 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7950-7959 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 7960-7969 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 7970-7979 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 7980-7989 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 7990-7999 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 8000-8009 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 8010-8019 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 8020-8029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 8030-8039 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8040-8049 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 8050-8059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 8060-8069 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8070-8079 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 8080-8089 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 8090-8099 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8100-8109 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 8110-8119 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 8120-8129 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 8130-8139 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 8140-8149 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8150-8159 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 8160-8169 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 8170-8179 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8180-8189 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 8190-8199 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 8200-8209 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 8210-8219 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8220-8229 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 8230-8239 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 8240-8249 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8250-8259 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 8260-8269 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 8270-8279 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 8280-8289 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 8290-8299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8300-8309 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 8310-8319 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 8320-8329 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 8330-8339 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8340-8349 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 8350-8359 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 8360-8369 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 8370-8379 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 30, // 8380-8389 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 8390-8399 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 8400-8409 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 8410-8419 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 8420-8429 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8430-8439 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 8440-8449 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8450-8459 + 1, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 8460-8469 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 8470-8479 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 8480-8489 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8490-8499 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8500-8509 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 8510-8519 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 8520-8529 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 8530-8539 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 8540-8549 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8550-8559 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 8560-8569 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 8570-8579 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 8580-8589 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 8590-8599 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 8600-8609 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8610-8619 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 8620-8629 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8630-8639 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 8640-8649 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8650-8659 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 8660-8669 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 8670-8679 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 8680-8689 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 8690-8699 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 8700-8709 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 8710-8719 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8720-8729 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 8730-8739 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 8740-8749 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 8750-8759 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 8760-8769 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 8770-8779 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 8780-8789 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8790-8799 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 8800-8809 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 8810-8819 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8820-8829 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 8830-8839 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 8840-8849 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8850-8859 + 1, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 8860-8869 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 8870-8879 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 8880-8889 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 8890-8899 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 8900-8909 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8910-8919 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 8920-8929 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 8930-8939 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 8940-8949 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 8950-8959 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 8960-8969 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 8970-8979 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 8980-8989 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 8990-8999 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 9000-9009 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 9010-9019 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 9020-9029 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9030-9039 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 9040-9049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 9050-9059 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 9060-9069 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 9070-9079 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9080-9089 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 9090-9099 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 9100-9109 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 9110-9119 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 9120-9129 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 9130-9139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9140-9149 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 9150-9159 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 9160-9169 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 9170-9179 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 9180-9189 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 9190-9199 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 9200-9209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9210-9219 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 9220-9229 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 9230-9239 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 9240-9249 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 9250-9259 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 9260-9269 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 9270-9279 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 9280-9289 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 9290-9299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9300-9309 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 9310-9319 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 9320-9329 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 9330-9339 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 9340-9349 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 9350-9359 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9360-9369 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 9370-9379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9380-9389 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 9390-9399 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 9400-9409 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 9410-9419 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9420-9429 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 22, // 9430-9439 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 9440-9449 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9450-9459 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 9460-9469 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 9470-9479 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9480-9489 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 9490-9499 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9500-9509 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9510-9519 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 9520-9529 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 9530-9539 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 9540-9549 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 9550-9559 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 9560-9569 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 9570-9579 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 9580-9589 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9590-9599 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 9600-9609 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 9610-9619 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 9620-9629 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 9630-9639 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 9640-9649 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9650-9659 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 9660-9669 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 9670-9679 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 9680-9689 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 9690-9699 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 9700-9709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 9710-9719 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 9720-9729 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 9730-9739 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 9740-9749 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 9750-9759 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 9760-9769 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9770-9779 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 9780-9789 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 9790-9799 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 9800-9809 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 9810-9819 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 9820-9829 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 9830-9839 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9840-9849 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 9850-9859 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9860-9869 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 9870-9879 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 9880-9889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9890-9899 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 9900-9909 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 9910-9919 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 9920-9929 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 9930-9939 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 9940-9949 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 9950-9959 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 9960-9969 + 3, 2, 1, 34, 33, 32, 31, 30, 29, 28, // 9970-9979 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 9980-9989 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 9990-9999 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 28, // 10000-10009 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 10010-10019 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 10020-10029 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 10030-10039 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 10040-10049 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10050-10059 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 10060-10069 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 10070-10079 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10080-10089 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 10090-10099 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 10100-10109 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 10110-10119 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10120-10129 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 10130-10139 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10140-10149 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 10150-10159 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 10160-10169 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 10170-10179 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10180-10189 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 10190-10199 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10200-10209 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10210-10219 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 10220-10229 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10230-10239 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 10240-10249 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 10250-10259 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 10260-10269 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 10270-10279 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 10280-10289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10290-10299 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 10300-10309 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 10310-10319 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10320-10329 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 10330-10339 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 10340-10349 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 10350-10359 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 10360-10369 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 10370-10379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10380-10389 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 28, // 10390-10399 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 10400-10409 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 10410-10419 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 10420-10429 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 10430-10439 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10440-10449 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 10450-10459 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 10460-10469 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 10470-10479 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 10480-10489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 10490-10499 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10500-10509 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 10510-10519 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 10520-10529 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 10530-10539 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 10540-10549 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 10550-10559 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 10560-10569 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 10570-10579 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 10580-10589 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 10590-10599 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 10600-10609 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 10610-10619 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 10620-10629 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 10630-10639 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10640-10649 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 10650-10659 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 10660-10669 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 10670-10679 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 10680-10689 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 10690-10699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 10700-10709 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10710-10719 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 10720-10729 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 10730-10739 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10740-10749 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 10750-10759 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10760-10769 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10770-10779 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 10780-10789 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 32, // 10790-10799 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 10800-10809 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 10810-10819 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 10820-10829 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 10830-10839 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 10840-10849 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 10850-10859 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 10860-10869 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10870-10879 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 10880-10889 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10890-10899 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 28, // 10900-10909 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 10910-10919 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 10920-10929 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 10930-10939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 10940-10949 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 10950-10959 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 10960-10969 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 10970-10979 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 10980-10989 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 10990-10999 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 11000-11009 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11010-11019 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 11020-11029 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11030-11039 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 11040-11049 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 11050-11059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 11060-11069 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11070-11079 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 11080-11089 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 11090-11099 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11100-11109 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 11110-11119 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 11120-11129 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 11130-11139 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 11140-11149 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 11150-11159 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 11160-11169 + 1, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 11170-11179 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11180-11189 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 11190-11199 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11200-11209 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 11210-11219 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 11220-11229 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 11230-11239 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 11240-11249 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 11250-11259 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11260-11269 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 11270-11279 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 11280-11289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 11290-11299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 11300-11309 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 11310-11319 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 11320-11329 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 11330-11339 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 11340-11349 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 11350-11359 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 11360-11369 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11370-11379 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 11380-11389 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 11390-11399 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 11400-11409 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11410-11419 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 11420-11429 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 11430-11439 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 11440-11449 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11450-11459 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 11460-11469 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11470-11479 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 11480-11489 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 11490-11499 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 11500-11509 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 11510-11519 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 11520-11529 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 11530-11539 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 11540-11549 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 11550-11559 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 11560-11569 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 11570-11579 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 11580-11589 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 11590-11599 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11600-11609 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 11610-11619 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11620-11629 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 11630-11639 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11640-11649 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 11650-11659 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11660-11669 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 11670-11679 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 11680-11689 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 11690-11699 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11700-11709 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 11710-11719 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 11720-11729 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11730-11739 + 3, 2, 1, 34, 33, 32, 31, 30, 29, 28, // 11740-11749 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 11750-11759 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11760-11769 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 11770-11779 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 11780-11789 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 11790-11799 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 11800-11809 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 11810-11819 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 11820-11829 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 24, // 11830-11839 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 11840-11849 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11850-11859 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 11860-11869 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11870-11879 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 11880-11889 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 11890-11899 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 11900-11909 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11910-11919 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 11920-11929 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 11930-11939 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 11940-11949 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 11950-11959 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 11960-11969 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 11970-11979 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 11980-11989 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 11990-11999 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 12000-12009 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 12010-12019 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 12020-12029 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 12030-12039 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 12040-12049 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 12050-12059 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12060-12069 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 12070-12079 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 12080-12089 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 12090-12099 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 12100-12109 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 24, // 12110-12119 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 12120-12129 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 12130-12139 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 12140-12149 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 12150-12159 + 1, 2, 1, 34, 33, 32, 31, 30, 29, 28, // 12160-12169 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 12170-12179 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 12180-12189 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 12190-12199 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 12200-12209 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 12210-12219 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 12220-12229 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 12230-12239 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12240-12249 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 12250-12259 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 12260-12269 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 12270-12279 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 12280-12289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12290-12299 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 12300-12309 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 12310-12319 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 12320-12329 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 12330-12339 + 3, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 12340-12349 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 12350-12359 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 12360-12369 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 12370-12379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12380-12389 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12390-12399 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 12400-12409 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 12410-12419 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 12420-12429 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 12430-12439 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12440-12449 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 12450-12459 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 12460-12469 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 12470-12479 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 12480-12489 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 12490-12499 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 12500-12509 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 12510-12519 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 12520-12529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 12530-12539 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 12540-12549 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 12550-12559 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 12560-12569 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 12570-12579 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 12580-12589 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12590-12599 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12600-12609 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 12610-12619 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 12620-12629 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 12630-12639 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 12640-12649 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 12650-12659 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12660-12669 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 12670-12679 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 12680-12689 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 12690-12699 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 12700-12709 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 12710-12719 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 12720-12729 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 12730-12739 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 12740-12749 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 12750-12759 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 12760-12769 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12770-12779 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12780-12789 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 12790-12799 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 12800-12809 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12810-12819 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 12820-12829 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12830-12839 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 12840-12849 + 3, 2, 1, 36, 35, 34, 33, 32, 31, 30, // 12850-12859 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 12860-12869 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 12870-12879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 12880-12889 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 12890-12899 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 12900-12909 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 12910-12919 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 12920-12929 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12930-12939 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 12940-12949 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 12950-12959 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 12960-12969 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 12970-12979 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 12980-12989 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 12990-12999 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 24, // 13000-13009 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 13010-13019 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 13020-13029 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 13030-13039 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 13040-13049 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 13050-13059 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 13060-13069 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 13070-13079 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 13080-13089 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 13090-13099 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 13100-13109 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13110-13119 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 13120-13129 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 13130-13139 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 13140-13149 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 13150-13159 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 13160-13169 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 13170-13179 + 3, 2, 1, 4, 3, 2, 1, 30, 29, 28, // 13180-13189 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 13190-13199 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 13200-13209 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 13210-13219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 13220-13229 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13230-13239 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 13240-13249 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 13250-13259 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 13260-13269 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 13270-13279 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13280-13289 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 13290-13299 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 13300-13309 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 13310-13319 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 13320-13329 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 28, // 13330-13339 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 13340-13349 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 13350-13359 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 13360-13369 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13370-13379 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 13380-13389 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 13390-13399 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13400-13409 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 13410-13419 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 13420-13429 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13430-13439 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13440-13449 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 13450-13459 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 13460-13469 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 13470-13479 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 13480-13489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 13490-13499 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 13500-13509 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 13510-13519 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 13520-13529 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 13530-13539 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 13540-13549 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 13550-13559 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 13560-13569 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 13570-13579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13580-13589 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 13590-13599 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 13600-13609 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 13610-13619 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 13620-13629 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 13630-13639 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 13640-13649 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 13650-13659 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 13660-13669 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 13670-13679 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 13680-13689 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 13690-13699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 13700-13709 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13710-13719 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 13720-13729 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 13730-13739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13740-13749 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 13750-13759 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 13760-13769 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13770-13779 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 13780-13789 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 13790-13799 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 13800-13809 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 13810-13819 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 13820-13829 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13830-13839 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 13840-13849 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 13850-13859 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 13860-13869 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 13870-13879 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 13880-13889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13890-13899 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 13900-13909 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 13910-13919 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 13920-13929 + 1, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 13930-13939 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 13940-13949 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 13950-13959 + 3, 2, 1, 4, 3, 2, 1, 30, 29, 28, // 13960-13969 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 13970-13979 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 13980-13989 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 13990-13999 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 14000-14009 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 14010-14019 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 14020-14029 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 14030-14039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14040-14049 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 14050-14059 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14060-14069 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14070-14079 + 1, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 14080-14089 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 14090-14099 + 7, 6, 5, 4, 3, 2, 1, 36, 35, 34, // 14100-14109 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 14110-14119 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 14120-14129 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14130-14139 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 14140-14149 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 14150-14159 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14160-14169 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 14170-14179 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 14180-14189 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 14190-14199 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 14200-14209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14210-14219 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 14220-14229 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14230-14239 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 14240-14249 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 14250-14259 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 14260-14269 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14270-14279 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14280-14289 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 14290-14299 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 14300-14309 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14310-14319 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 14320-14329 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14330-14339 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 14340-14349 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 14350-14359 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 14360-14369 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 14370-14379 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 14380-14389 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14390-14399 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 14400-14409 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 14410-14419 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 14420-14429 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 14430-14439 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 14440-14449 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14450-14459 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 14460-14469 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 14470-14479 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 14480-14489 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14490-14499 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 14500-14509 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 14510-14519 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14520-14529 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 14530-14539 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 14540-14549 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 14550-14559 + 1, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 14560-14569 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 14570-14579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14580-14589 + 1, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 14590-14599 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 14600-14609 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 14610-14619 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 14620-14629 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 14630-14639 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14640-14649 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 14650-14659 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 14660-14669 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14670-14679 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 14680-14689 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 14690-14699 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14700-14709 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 14710-14719 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 14720-14729 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 14730-14739 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 14740-14749 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 14750-14759 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 14760-14769 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 14770-14779 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 14780-14789 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 14790-14799 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14800-14809 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 14810-14819 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 14820-14829 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14830-14839 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 14840-14849 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 14850-14859 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 14860-14869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 14870-14879 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 14880-14889 + 1, 6, 5, 4, 3, 2, 1, 26, 25, 24, // 14890-14899 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 14900-14909 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14910-14919 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 14920-14929 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 14930-14939 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 14940-14949 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 14950-14959 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 14960-14969 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 14970-14979 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 14980-14989 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 14990-14999 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 15000-15009 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 15010-15019 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15020-15029 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 15030-15039 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 15040-15049 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 15050-15059 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 15060-15069 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 15070-15079 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 15080-15089 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15090-15099 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 15100-15109 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15110-15119 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15120-15129 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 15130-15139 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 15140-15149 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15150-15159 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 15160-15169 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 15170-15179 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 15180-15189 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 15190-15199 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 15200-15209 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 15210-15219 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 15220-15229 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 15230-15239 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 15240-15249 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 15250-15259 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 15260-15269 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 15270-15279 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 15280-15289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 15290-15299 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 15300-15309 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 15310-15319 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 15320-15329 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 15330-15339 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 15340-15349 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 15350-15359 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 15360-15369 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 15370-15379 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 15380-15389 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15390-15399 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 15400-15409 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 15410-15419 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 15420-15429 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 15430-15439 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 15440-15449 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15450-15459 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 15460-15469 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 15470-15479 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 15480-15489 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 15490-15499 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15500-15509 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 15510-15519 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 15520-15529 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15530-15539 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15540-15549 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 15550-15559 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 15560-15569 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15570-15579 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 15580-15589 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15590-15599 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 15600-15609 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 15610-15619 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 15620-15629 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15630-15639 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 15640-15649 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15650-15659 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 15660-15669 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 15670-15679 + 3, 2, 1, 44, 43, 42, 41, 40, 39, 38, // 15680-15689 + 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 15690-15699 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 15700-15709 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 15710-15719 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 15720-15729 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 15730-15739 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 15740-15749 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15750-15759 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 15760-15769 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 15770-15779 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 15780-15789 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 15790-15799 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 15800-15809 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 15810-15819 + 3, 2, 1, 36, 35, 34, 33, 32, 31, 30, // 15820-15829 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 15830-15839 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 15840-15849 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 15850-15859 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 15860-15869 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 15870-15879 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 15880-15889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15890-15899 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 15900-15909 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 15910-15919 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 15920-15929 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 15930-15939 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 15940-15949 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 15950-15959 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15960-15969 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 15970-15979 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15980-15989 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 15990-15999 + 1, 6, 5, 4, 3, 2, 1, 26, 25, 24, // 16000-16009 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 16010-16019 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 16020-16029 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 16030-16039 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 16040-16049 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 16050-16059 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 16060-16069 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 16070-16079 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 16080-16089 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 16090-16099 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 16100-16109 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 16110-16119 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 16120-16129 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 16130-16139 + 1, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 16140-16149 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 16150-16159 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 16160-16169 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 16170-16179 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 16180-16189 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 16190-16199 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 16200-16209 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 16210-16219 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 16220-16229 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 16230-16239 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 16240-16249 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 16250-16259 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 16260-16269 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 16270-16279 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 16280-16289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16290-16299 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 16300-16309 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 16310-16319 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 16320-16329 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 16330-16339 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 16340-16349 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16350-16359 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 16360-16369 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16370-16379 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 16380-16389 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 16390-16399 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16400-16409 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 16410-16419 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 16420-16429 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 16430-16439 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 16440-16449 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 16450-16459 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 16460-16469 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 16470-16479 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 16480-16489 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 16490-16499 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 16500-16509 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 16510-16519 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 16520-16529 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 16530-16539 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 16540-16549 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 16550-16559 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 16560-16569 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 16570-16579 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 16580-16589 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 16590-16599 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 16600-16609 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 16610-16619 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16620-16629 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 16630-16639 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 16640-16649 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 16650-16659 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 16660-16669 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 16670-16679 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16680-16689 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 16690-16699 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 16700-16709 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 16710-16719 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 16720-16729 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16730-16739 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 16740-16749 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 16750-16759 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 16760-16769 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 16770-16779 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 16780-16789 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 16790-16799 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16800-16809 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 16810-16819 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 16820-16829 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 16830-16839 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 16840-16849 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 16850-16859 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16860-16869 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 16870-16879 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 16880-16889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16890-16899 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 16900-16909 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 16910-16919 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 16920-16929 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 16930-16939 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 16940-16949 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 16950-16959 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 16960-16969 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 16970-16979 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 16980-16989 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 16990-16999 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17000-17009 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17010-17019 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 17020-17029 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 17030-17039 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 17040-17049 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 17050-17059 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17060-17069 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 17070-17079 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 17080-17089 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 17090-17099 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 17100-17109 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 17110-17119 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 17120-17129 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 17130-17139 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 17140-17149 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 17150-17159 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 17160-17169 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 17170-17179 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 17180-17189 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 17190-17199 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 22, // 17200-17209 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 17210-17219 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17220-17229 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 17230-17239 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17240-17249 + 7, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 17250-17259 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 17260-17269 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 17270-17279 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17280-17289 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 17290-17299 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17300-17309 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 17310-17319 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 17320-17329 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 17330-17339 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17340-17349 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 17350-17359 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17360-17369 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 17370-17379 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 17380-17389 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 17390-17399 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17400-17409 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 17410-17419 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17420-17429 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 17430-17439 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 17440-17449 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17450-17459 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 17460-17469 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 17470-17479 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 17480-17489 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 17490-17499 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 17500-17509 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 17510-17519 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 17520-17529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 17530-17539 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17540-17549 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 17550-17559 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 17560-17569 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 17570-17579 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17580-17589 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 17590-17599 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 17600-17609 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 17610-17619 + 3, 2, 1, 4, 3, 2, 1, 30, 29, 28, // 17620-17629 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 17630-17639 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17640-17649 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 17650-17659 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 17660-17669 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17670-17679 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 17680-17689 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17690-17699 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 17700-17709 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 17710-17719 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 17720-17729 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 17730-17739 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 17740-17749 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17750-17759 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 17760-17769 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 17770-17779 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 17780-17789 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17790-17799 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 17800-17809 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17810-17819 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 17820-17829 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 17830-17839 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17840-17849 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 17850-17859 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 17860-17869 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17870-17879 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17880-17889 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 17890-17899 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 17900-17909 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17910-17919 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 17920-17929 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 17930-17939 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 17940-17949 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 17950-17959 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 17960-17969 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 17970-17979 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 24, // 17980-17989 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 17990-17999 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18000-18009 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 18010-18019 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 18020-18029 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18030-18039 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 18040-18049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 18050-18059 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 18060-18069 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 18070-18079 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 18080-18089 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 18090-18099 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 18100-18109 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 18110-18119 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 18120-18129 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 18130-18139 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 20, // 18140-18149 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 18150-18159 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 18160-18169 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18170-18179 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18180-18189 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 18190-18199 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18200-18209 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 18210-18219 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 18220-18229 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 18230-18239 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18240-18249 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 18250-18259 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 18260-18269 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 18270-18279 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 18280-18289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18290-18299 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 18300-18309 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 18310-18319 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 18320-18329 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18330-18339 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18340-18349 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 18350-18359 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 18360-18369 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 18370-18379 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 18380-18389 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 18390-18399 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18400-18409 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 18410-18419 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 18420-18429 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 18430-18439 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 18440-18449 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 18450-18459 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 18460-18469 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18470-18479 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18480-18489 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 18490-18499 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 18500-18509 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 18510-18519 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 18520-18529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 18530-18539 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18540-18549 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 18550-18559 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 18560-18569 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18570-18579 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 18580-18589 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 18590-18599 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 18600-18609 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 18610-18619 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 18620-18629 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 18630-18639 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 18640-18649 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18650-18659 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18660-18669 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 18670-18679 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18680-18689 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18690-18699 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18700-18709 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 18710-18719 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18720-18729 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18730-18739 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 18740-18749 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 18750-18759 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18760-18769 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 18770-18779 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 18780-18789 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 18790-18799 + 3, 2, 1, 36, 35, 34, 33, 32, 31, 30, // 18800-18809 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 18810-18819 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 18820-18829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 18830-18839 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 18840-18849 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 18850-18859 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 30, // 18860-18869 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 18870-18879 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 18880-18889 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 18890-18899 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18900-18909 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 28, // 18910-18919 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 18920-18929 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 18930-18939 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 18940-18949 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 18950-18959 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 18960-18969 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 18970-18979 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 18980-18989 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 18990-18999 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 19000-19009 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 19010-19019 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19020-19029 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 19030-19039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19040-19049 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 19050-19059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 19060-19069 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 19070-19079 + 1, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 19080-19089 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 19090-19099 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 19100-19109 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19110-19119 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 19120-19129 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 19130-19139 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 19140-19149 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 19150-19159 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 19160-19169 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19170-19179 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 19180-19189 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 19190-19199 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 19200-19209 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 19210-19219 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19220-19229 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 19230-19239 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 19240-19249 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 19250-19259 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 19260-19269 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 19270-19279 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 19280-19289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19290-19299 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 19300-19309 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 19310-19319 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 19320-19329 + 3, 2, 1, 40, 39, 38, 37, 36, 35, 34, // 19330-19339 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 19340-19349 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 19350-19359 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 19360-19369 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 19370-19379 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 19380-19389 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 19390-19399 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 19400-19409 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 19410-19419 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 19420-19429 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 19430-19439 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 19440-19449 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 19450-19459 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 19460-19469 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 19470-19479 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 19480-19489 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19490-19499 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 19500-19509 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 19510-19519 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19520-19529 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19530-19539 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 19540-19549 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 19550-19559 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19560-19569 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 19570-19579 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 19580-19589 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 19590-19599 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 52, // 19600-19609 + 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, // 19610-19619 + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 19620-19629 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 19630-19639 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 19640-19649 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19650-19659 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 19660-19669 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19670-19679 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 19680-19689 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 19690-19699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 19700-19709 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 19710-19719 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 19720-19729 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 19730-19739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19740-19749 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 19750-19759 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 19760-19769 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 19770-19779 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 19780-19789 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 19790-19799 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 19800-19809 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 19810-19819 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 19820-19829 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19830-19839 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 19840-19849 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 19850-19859 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 19860-19869 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 19870-19879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 19880-19889 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 19890-19899 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 19900-19909 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 19910-19919 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 19920-19929 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 19930-19939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 19940-19949 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19950-19959 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 19960-19969 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 19970-19979 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 19980-19989 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 19990-19999 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20000-20009 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20010-20019 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 20020-20029 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 20030-20039 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 20040-20049 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20050-20059 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 20060-20069 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 20070-20079 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 20080-20089 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20090-20099 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 20100-20109 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 20110-20119 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 20120-20129 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20130-20139 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 20140-20149 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20150-20159 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20160-20169 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 20170-20179 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 20180-20189 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20190-20199 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 20200-20209 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 20210-20219 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20220-20229 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 20230-20239 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 20240-20249 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20250-20259 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 20260-20269 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 20270-20279 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 20280-20289 + 7, 6, 5, 4, 3, 2, 1, 26, 25, 24, // 20290-20299 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 20300-20309 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20310-20319 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 20320-20329 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 20330-20339 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 20340-20349 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 20350-20359 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 20360-20369 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 20370-20379 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 20380-20389 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 20390-20399 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 20400-20409 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 20410-20419 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20420-20429 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20430-20439 + 1, 2, 1, 34, 33, 32, 31, 30, 29, 28, // 20440-20449 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 20450-20459 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 20460-20469 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 20470-20479 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 20480-20489 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 20490-20499 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 20500-20509 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20510-20519 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20520-20529 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 20530-20539 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 20540-20549 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20550-20559 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 20560-20569 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 20570-20579 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20580-20589 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 20590-20599 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20600-20609 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 20610-20619 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 20620-20629 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 20630-20639 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 20640-20649 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20650-20659 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 20660-20669 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20670-20679 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20680-20689 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 20690-20699 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 20700-20709 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 20710-20719 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20720-20729 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20730-20739 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 20740-20749 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 20750-20759 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20760-20769 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 20770-20779 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 20780-20789 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 20790-20799 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 40, // 20800-20809 + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 20810-20819 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 20820-20829 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 20830-20839 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 20840-20849 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 20850-20859 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 20860-20869 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 20870-20879 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 20880-20889 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 20890-20899 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 20900-20909 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20910-20919 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 20920-20929 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 20930-20939 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 20940-20949 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 20950-20959 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 20960-20969 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20970-20979 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 20980-20989 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 20990-20999 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21000-21009 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 21010-21019 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 21020-21029 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 21030-21039 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 21040-21049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 21050-21059 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 21060-21069 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 21070-21079 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 21080-21089 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21090-21099 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 21100-21109 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21110-21119 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 21120-21129 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 21130-21139 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 21140-21149 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 21150-21159 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 21160-21169 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 21170-21179 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 21180-21189 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 21190-21199 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21200-21209 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21210-21219 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 21220-21229 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 21230-21239 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 21240-21249 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 21250-21259 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 21260-21269 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 21270-21279 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 21280-21289 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 21290-21299 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 21300-21309 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 21310-21319 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 21320-21329 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21330-21339 + 1, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 21340-21349 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 21350-21359 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 21360-21369 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 21370-21379 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 21380-21389 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 21390-21399 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 21400-21409 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 21410-21419 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 21420-21429 + 3, 2, 1, 34, 33, 32, 31, 30, 29, 28, // 21430-21439 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 21440-21449 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 21450-21459 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 21460-21469 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21470-21479 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 21480-21489 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 21490-21499 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 21500-21509 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 21510-21519 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 28, // 21520-21529 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 21530-21539 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 21540-21549 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 21550-21559 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 21560-21569 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 21570-21579 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 21580-21589 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 21590-21599 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21600-21609 + 1, 2, 1, 4, 3, 2, 1, 30, 29, 28, // 21610-21619 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 21620-21629 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 21630-21639 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 21640-21649 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21650-21659 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 21660-21669 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 21670-21679 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 21680-21689 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21690-21699 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 21700-21709 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 21710-21719 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 21720-21729 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 21730-21739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21740-21749 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 21750-21759 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 21760-21769 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 21770-21779 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 21780-21789 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 21790-21799 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 21800-21809 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 21810-21819 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 21820-21829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 21830-21839 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21840-21849 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 21850-21859 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 21860-21869 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21870-21879 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 21880-21889 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 21890-21899 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21900-21909 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 21910-21919 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 21920-21929 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 21930-21939 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 21940-21949 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21950-21959 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 21960-21969 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 21970-21979 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 21980-21989 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 21990-21999 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 22000-22009 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 22010-22019 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 22020-22029 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 22030-22039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22040-22049 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 22050-22059 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 22060-22069 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 22070-22079 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22080-22089 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 22090-22099 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 22100-22109 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 22110-22119 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 22120-22129 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 22130-22139 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 22140-22149 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 22150-22159 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22160-22169 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 22170-22179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 22180-22189 + 3, 2, 1, 36, 35, 34, 33, 32, 31, 30, // 22190-22199 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 22200-22209 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 22210-22219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 22220-22229 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 22230-22239 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 22240-22249 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 22250-22259 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22260-22269 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 22270-22279 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 22280-22289 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 22290-22299 + 3, 2, 1, 4, 3, 2, 1, 36, 35, 34, // 22300-22309 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 22310-22319 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 22320-22329 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 22330-22339 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 22340-22349 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 22350-22359 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 22360-22369 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22370-22379 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22380-22389 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 22390-22399 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 22400-22409 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 22410-22419 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 22420-22429 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 22430-22439 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 22440-22449 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 22450-22459 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 22460-22469 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22470-22479 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 22480-22489 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22490-22499 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22500-22509 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 22510-22519 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22520-22529 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22530-22539 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 22540-22549 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 22550-22559 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 22560-22569 + 1, 2, 1, 40, 39, 38, 37, 36, 35, 34, // 22570-22579 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 22580-22589 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 22590-22599 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 22600-22609 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 22610-22619 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 22620-22629 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 22630-22639 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 22640-22649 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 22650-22659 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 22660-22669 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 22670-22679 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22680-22689 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 22690-22699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 22700-22709 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 22710-22719 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 22720-22729 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 22730-22739 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22740-22749 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 22750-22759 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 22760-22769 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 22770-22779 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 22780-22789 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 22790-22799 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 22800-22809 + 1, 6, 5, 4, 3, 2, 1, 36, 35, 34, // 22810-22819 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 22820-22829 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 22830-22839 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 22840-22849 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 22850-22859 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22860-22869 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 22870-22879 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 22880-22889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22890-22899 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 22900-22909 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22910-22919 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 22920-22929 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 22930-22939 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 22940-22949 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 22950-22959 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 22960-22969 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 22970-22979 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 22980-22989 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 22990-22999 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 23000-23009 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 23010-23019 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 23020-23029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 23030-23039 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 23040-23049 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 23050-23059 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 23060-23069 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23070-23079 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 23080-23089 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 23090-23099 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 23100-23109 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 23110-23119 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23120-23129 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 23130-23139 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 23140-23149 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 23150-23159 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 23160-23169 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 23170-23179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 23180-23189 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 23190-23199 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 23200-23209 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 23210-23219 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 23220-23229 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 23230-23239 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23240-23249 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 23250-23259 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 23260-23269 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 23270-23279 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23280-23289 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 23290-23299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23300-23309 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23310-23319 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 23320-23329 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 23330-23339 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 23340-23349 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 23350-23359 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 23360-23369 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 23370-23379 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 23380-23389 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 23390-23399 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 23400-23409 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 23410-23419 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23420-23429 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 23430-23439 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 23440-23449 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 23450-23459 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 23460-23469 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 23470-23479 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 23480-23489 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 23490-23499 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 23500-23509 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 23510-23519 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23520-23529 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 23530-23539 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 23540-23549 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 23550-23559 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 23560-23569 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23570-23579 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 23580-23589 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 23590-23599 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 23600-23609 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 23610-23619 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 23620-23629 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 23630-23639 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 23640-23649 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 23650-23659 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 23660-23669 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 23670-23679 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 30, // 23680-23689 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 23690-23699 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 23700-23709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 23710-23719 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 23720-23729 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23730-23739 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 23740-23749 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 23750-23759 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 23760-23769 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 23770-23779 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 23780-23789 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23790-23799 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 23800-23809 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 23810-23819 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 23820-23829 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 23830-23839 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 23840-23849 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 23850-23859 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 23860-23869 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 23870-23879 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 23880-23889 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 23890-23899 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 23900-23909 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 23910-23919 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 28, // 23920-23929 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 23930-23939 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 23940-23949 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 23950-23959 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 23960-23969 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 23970-23979 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 23980-23989 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 23990-23999 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 24000-24009 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 24010-24019 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 24020-24029 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 24030-24039 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 24040-24049 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24050-24059 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24060-24069 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 24070-24079 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 24080-24089 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 24090-24099 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 24100-24109 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 24110-24119 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 24120-24129 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 24130-24139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24140-24149 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 24150-24159 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 24160-24169 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 24170-24179 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 24180-24189 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 24190-24199 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 24200-24209 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 24210-24219 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 24220-24229 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 24230-24239 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 24240-24249 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 24250-24259 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 24260-24269 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24270-24279 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 24280-24289 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 24290-24299 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 24300-24309 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 24310-24319 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 24320-24329 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 24330-24339 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 24340-24349 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 24350-24359 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24360-24369 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 24370-24379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24380-24389 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 24390-24399 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 24400-24409 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 24410-24419 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 24420-24429 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 24430-24439 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 24440-24449 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 24450-24459 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 24460-24469 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 24470-24479 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 24480-24489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 24490-24499 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 24500-24509 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 24510-24519 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 24520-24529 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 24530-24539 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 24540-24549 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 24550-24559 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24560-24569 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 24570-24579 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 24580-24589 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 24590-24599 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24600-24609 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 24610-24619 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 24620-24629 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 24630-24639 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 24640-24649 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 24650-24659 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24660-24669 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 24670-24679 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 24680-24689 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 24690-24699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 24700-24709 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 24710-24719 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 24720-24729 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 24730-24739 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 24740-24749 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 24750-24759 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 24760-24769 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24770-24779 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 24780-24789 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 24790-24799 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 24800-24809 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24810-24819 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 24820-24829 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 24830-24839 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 24840-24849 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 24850-24859 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 24860-24869 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 24870-24879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 24880-24889 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 24890-24899 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 24900-24909 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 24910-24919 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 24920-24929 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 24930-24939 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 24940-24949 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 24950-24959 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 24960-24969 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 24970-24979 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 24980-24989 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 24990-24999 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25000-25009 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 25010-25019 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25020-25029 + 1, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 25030-25039 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 25040-25049 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 25050-25059 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25060-25069 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 25070-25079 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 25080-25089 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 25090-25099 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25100-25109 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 25110-25119 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 25120-25129 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 25130-25139 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 25140-25149 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 25150-25159 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 25160-25169 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25170-25179 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 30, // 25180-25189 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 25190-25199 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 25200-25209 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 25210-25219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 25220-25229 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 25230-25239 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 25240-25249 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 25250-25259 + 1, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 25260-25269 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 25270-25279 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 25280-25289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25290-25299 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 25300-25309 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25310-25319 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 25320-25329 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 25330-25339 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 25340-25349 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 25350-25359 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 25360-25369 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 25370-25379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25380-25389 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 25390-25399 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 25400-25409 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25410-25419 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 25420-25429 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 25430-25439 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 25440-25449 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 25450-25459 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 25460-25469 + 1, 52, 51, 50, 49, 48, 47, 46, 45, 44, // 25470-25479 + 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 25480-25489 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 25490-25499 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 25500-25509 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25510-25519 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 25520-25529 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 25530-25539 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 25540-25549 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25550-25559 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 25560-25569 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 25570-25579 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 25580-25589 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25590-25599 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 25600-25609 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25610-25619 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25620-25629 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 25630-25639 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 25640-25649 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 25650-25659 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 25660-25669 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 25670-25679 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25680-25689 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 25690-25699 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 25700-25709 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 25710-25719 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25720-25729 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 25730-25739 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 25740-25749 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 25750-25759 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 25760-25769 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 25770-25779 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25780-25789 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 25790-25799 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 25800-25809 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 25810-25819 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 25820-25829 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25830-25839 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 25840-25849 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 25850-25859 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 25860-25869 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 25870-25879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 25880-25889 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 25890-25899 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 25900-25909 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 25910-25919 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25920-25929 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 25930-25939 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 25940-25949 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 25950-25959 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 25960-25969 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 25970-25979 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 25980-25989 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 25990-25999 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 26000-26009 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 26010-26019 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 26020-26029 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26030-26039 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 26040-26049 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 26050-26059 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 26060-26069 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 26070-26079 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 26080-26089 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 26090-26099 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 26100-26109 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 26110-26119 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 26120-26129 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26130-26139 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 26140-26149 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 26150-26159 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26160-26169 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 26170-26179 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 26180-26189 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 26190-26199 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 26200-26209 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 26210-26219 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 26220-26229 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 26230-26239 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 26240-26249 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26250-26259 + 1, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 26260-26269 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 26270-26279 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 26280-26289 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 26290-26299 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 26300-26309 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 26310-26319 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 26320-26329 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 26330-26339 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 26340-26349 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 26350-26359 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26360-26369 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 26370-26379 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 26380-26389 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 26390-26399 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 26400-26409 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 26410-26419 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 26420-26429 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 26430-26439 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 26440-26449 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 26450-26459 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 26460-26469 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 26470-26479 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 26480-26489 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 26490-26499 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 26500-26509 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 26510-26519 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 26520-26529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 26530-26539 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 26540-26549 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 26550-26559 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 26560-26569 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 26570-26579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26580-26589 + 1, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 26590-26599 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 26600-26609 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 26610-26619 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 26620-26629 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 26630-26639 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 26640-26649 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 26650-26659 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 26660-26669 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26670-26679 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 26680-26689 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 26690-26699 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26700-26709 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 26710-26719 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 26720-26729 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 26730-26739 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 26740-26749 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 26750-26759 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 26760-26769 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 26770-26779 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 26780-26789 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26790-26799 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 26800-26809 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 26810-26819 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 26820-26829 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 26830-26839 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 26840-26849 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26850-26859 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 26860-26869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 26870-26879 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26880-26889 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 26890-26899 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 26900-26909 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26910-26919 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 26920-26929 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 26930-26939 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 26940-26949 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 26950-26959 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 26960-26969 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 26970-26979 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 26980-26989 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 26990-26999 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27000-27009 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 27010-27019 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27020-27029 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 27030-27039 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 27040-27049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 27050-27059 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 27060-27069 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 27070-27079 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27080-27089 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 27090-27099 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 18, // 27100-27109 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 27110-27119 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 27120-27129 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 27130-27139 + 3, 2, 1, 36, 35, 34, 33, 32, 31, 30, // 27140-27149 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 27150-27159 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 27160-27169 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 27170-27179 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27180-27189 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 27190-27199 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27200-27209 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 27210-27219 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 27220-27229 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 27230-27239 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 27240-27249 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 27250-27259 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27260-27269 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 27270-27279 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 27280-27289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 30, // 27290-27299 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 27300-27309 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 27310-27319 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 27320-27329 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 27330-27339 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 27340-27349 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27350-27359 + 1, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 27360-27369 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 27370-27379 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 27380-27389 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 27390-27399 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 27400-27409 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 27410-27419 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 27420-27429 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 27430-27439 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 27440-27449 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 27450-27459 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 27460-27469 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 27470-27479 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 27480-27489 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 27490-27499 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 27500-27509 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 27510-27519 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 27520-27529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 27530-27539 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27540-27549 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 27550-27559 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 27560-27569 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27570-27579 + 1, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 27580-27589 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 27590-27599 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27600-27609 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 27610-27619 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27620-27629 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 27630-27639 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 27640-27649 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 27650-27659 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 27660-27669 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 27670-27679 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 27680-27689 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 27690-27699 + 1, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 27700-27709 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 27710-27719 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 27720-27729 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 27730-27739 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 27740-27749 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 27750-27759 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 27760-27769 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 27770-27779 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27780-27789 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 27790-27799 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 27800-27809 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 27810-27819 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 27820-27829 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 27830-27839 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 27840-27849 + 1, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 27850-27859 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 27860-27869 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 27870-27879 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 27880-27889 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 27890-27899 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 27900-27909 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 27910-27919 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 27920-27929 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 27930-27939 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 27940-27949 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 27950-27959 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 27960-27969 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 27970-27979 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 27980-27989 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 27990-27999 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 28000-28009 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 28010-28019 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 28020-28029 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 28030-28039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28040-28049 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 28050-28059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 28060-28069 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28070-28079 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 28080-28089 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 28090-28099 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 28100-28109 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 28110-28119 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 28120-28129 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 28130-28139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28140-28149 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 28150-28159 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 28160-28169 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28170-28179 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 28180-28189 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28190-28199 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28200-28209 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 28210-28219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 48, // 28220-28229 + 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, // 28230-28239 + 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 28240-28249 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 28250-28259 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 28260-28269 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 28270-28279 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 28280-28289 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 28290-28299 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 28300-28309 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 30, // 28310-28319 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 28320-28329 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 28330-28339 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 28340-28349 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 28350-28359 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 28360-28369 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 28370-28379 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 28380-28389 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 28390-28399 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 28400-28409 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 28410-28419 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 28420-28429 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 28430-28439 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 28440-28449 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 28450-28459 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 28460-28469 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 28470-28479 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 28480-28489 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 28490-28499 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 28500-28509 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 28510-28519 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 28520-28529 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 28530-28539 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 28540-28549 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 28550-28559 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28560-28569 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 28570-28579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28580-28589 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 28590-28599 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 28600-28609 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 28610-28619 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 28620-28629 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 28630-28639 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 28640-28649 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 28650-28659 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 28660-28669 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 28670-28679 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 28680-28689 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 28690-28699 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 28700-28709 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 28710-28719 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 28720-28729 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 28730-28739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28740-28749 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 28750-28759 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28760-28769 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 28770-28779 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 28780-28789 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 28790-28799 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 28800-28809 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 28810-28819 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 28820-28829 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 28830-28839 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 28840-28849 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 28850-28859 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 28860-28869 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 28870-28879 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 28880-28889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28890-28899 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 28900-28909 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28910-28919 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 28920-28929 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 28930-28939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 28940-28949 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 28950-28959 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 28960-28969 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 30, // 28970-28979 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 28980-28989 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 28990-28999 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 29000-29009 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 29010-29019 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 29020-29029 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 29030-29039 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 29040-29049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 29050-29059 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 29060-29069 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 29070-29079 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 29080-29089 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29090-29099 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 29100-29109 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29110-29119 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 29120-29129 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 29130-29139 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 29140-29149 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 29150-29159 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 29160-29169 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 29170-29179 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29180-29189 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29190-29199 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 29200-29209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29210-29219 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29220-29229 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29230-29239 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 29240-29249 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 29250-29259 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 29260-29269 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 29270-29279 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 29280-29289 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 29290-29299 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 29300-29309 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 29310-29319 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 29320-29329 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 29330-29339 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 29340-29349 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29350-29359 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 29360-29369 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29370-29379 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 29380-29389 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 29390-29399 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29400-29409 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29410-29419 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 29420-29429 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 29430-29439 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 29440-29449 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 29450-29459 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29460-29469 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 29470-29479 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 29480-29489 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29490-29499 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 29500-29509 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 29510-29519 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 29520-29529 + 1, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 29530-29539 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 29540-29549 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 29550-29559 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 29560-29569 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 29570-29579 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 29580-29589 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 29590-29599 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29600-29609 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 29610-29619 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 29620-29629 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 29630-29639 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 29640-29649 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29650-29659 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 29660-29669 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29670-29679 + 3, 2, 1, 34, 33, 32, 31, 30, 29, 28, // 29680-29689 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 29690-29699 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 29700-29709 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 29710-29719 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 29720-29729 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29730-29739 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29740-29749 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 29750-29759 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 29760-29769 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 29770-29779 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 29780-29789 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29790-29799 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 29800-29809 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 29810-29819 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29820-29829 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 29830-29839 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 29840-29849 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29850-29859 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 29860-29869 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 29870-29879 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 29880-29889 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 29890-29899 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 29900-29909 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 29910-29919 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 29920-29929 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 29930-29939 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 29940-29949 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 29950-29959 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 29960-29969 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 29970-29979 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 29980-29989 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 29990-29999 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30000-30009 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 30010-30019 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 30020-30029 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 30030-30039 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 30040-30049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 30050-30059 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30060-30069 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 30070-30079 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 30080-30089 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 30090-30099 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 30100-30109 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 30110-30119 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 30120-30129 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 22, // 30130-30139 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 30140-30149 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30150-30159 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 30160-30169 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30170-30179 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 30180-30189 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 30190-30199 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 30200-30209 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 30210-30219 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 30220-30229 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30230-30239 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 30240-30249 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 30250-30259 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 30260-30269 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 30270-30279 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 30280-30289 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 30290-30299 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 30300-30309 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 30310-30319 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 30320-30329 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30330-30339 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 30340-30349 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 30350-30359 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 30360-30369 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 30370-30379 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 30380-30389 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 30390-30399 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 30400-30409 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 30410-30419 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 30420-30429 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 30430-30439 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 30440-30449 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 30450-30459 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 30460-30469 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 30470-30479 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30480-30489 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 30490-30499 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 30500-30509 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 30510-30519 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 30520-30529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 30530-30539 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 30540-30549 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 18, // 30550-30559 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 30560-30569 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 30570-30579 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 30580-30589 + 3, 2, 1, 38, 37, 36, 35, 34, 33, 32, // 30590-30599 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 30600-30609 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 30610-30619 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30620-30629 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 30630-30639 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 30640-30649 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30650-30659 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30660-30669 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 30670-30679 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 30680-30689 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 30690-30699 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 30700-30709 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 30710-30719 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 30720-30729 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 30730-30739 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 30740-30749 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 30750-30759 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 30760-30769 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 30770-30779 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 30780-30789 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 30790-30799 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 30800-30809 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 30810-30819 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 30820-30829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 30830-30839 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30840-30849 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 30850-30859 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 30860-30869 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30870-30879 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 30880-30889 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 30890-30899 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30900-30909 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 30910-30919 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30920-30929 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 30930-30939 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 30940-30949 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 30950-30959 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 30960-30969 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 30970-30979 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 30980-30989 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 30990-30999 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 31000-31009 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 31010-31019 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 31020-31029 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 31030-31039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31040-31049 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 31050-31059 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 31060-31069 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 31070-31079 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31080-31089 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 31090-31099 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 31100-31109 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31110-31119 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 31120-31129 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 31130-31139 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 31140-31149 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 31150-31159 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31160-31169 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 31170-31179 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 31180-31189 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 31190-31199 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 31200-31209 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 31210-31219 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 31220-31229 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 31230-31239 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 31240-31249 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 31250-31259 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 31260-31269 + 1, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 31270-31279 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 31280-31289 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31290-31299 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 31300-31309 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 31310-31319 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 31320-31329 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 31330-31339 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31340-31349 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 31350-31359 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 31360-31369 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 31370-31379 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 31380-31389 + 1, 2, 1, 4, 3, 2, 1, 72, 71, 70, // 31390-31399 + 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, // 31400-31409 + 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, // 31410-31419 + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, // 31420-31429 + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 31430-31439 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 31440-31449 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 31450-31459 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 31460-31469 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 31470-31479 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 31480-31489 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 31490-31499 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31500-31509 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 31510-31519 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31520-31529 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31530-31539 + 1, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 31540-31549 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31550-31559 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 31560-31569 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 31570-31579 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 31580-31589 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31590-31599 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 31600-31609 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31610-31619 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 31620-31629 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 31630-31639 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 31640-31649 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 31650-31659 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 31660-31669 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31670-31679 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 31680-31689 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 31690-31699 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 31700-31709 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31710-31719 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 31720-31729 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31730-31739 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31740-31749 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 31750-31759 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 31760-31769 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 31770-31779 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 31780-31789 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 31790-31799 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31800-31809 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 31810-31819 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 31820-31829 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31830-31839 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 31840-31849 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 31850-31859 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 31860-31869 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 31870-31879 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 31880-31889 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31890-31899 + 7, 6, 5, 4, 3, 2, 1, 50, 49, 48, // 31900-31909 + 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, // 31910-31919 + 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 31920-31929 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 31930-31939 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 31940-31949 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 31950-31959 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 31960-31969 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 31970-31979 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 31980-31989 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 31990-31999 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 32000-32009 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 32010-32019 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 32020-32029 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 32030-32039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32040-32049 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 32050-32059 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 32060-32069 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 32070-32079 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 32080-32089 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 32090-32099 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 32100-32109 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 32110-32119 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 32120-32129 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32130-32139 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 32140-32149 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 32150-32159 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 32160-32169 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 32170-32179 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 32180-32189 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 32190-32199 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 32200-32209 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 32210-32219 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 32220-32229 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 32230-32239 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32240-32249 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 32250-32259 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 32260-32269 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 32270-32279 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 32280-32289 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 32290-32299 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 32300-32309 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32310-32319 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 32320-32329 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32330-32339 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 32340-32349 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 32350-32359 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 32360-32369 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 32370-32379 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 32380-32389 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32390-32399 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32400-32409 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 32410-32419 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 32420-32429 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32430-32439 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 32440-32449 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 32450-32459 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 32460-32469 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 32470-32479 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32480-32489 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 32490-32499 + 3, 2, 1, 4, 3, 2, 1, 24, 23, 22, // 32500-32509 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 32510-32519 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32520-32529 + 1, 2, 1, 4, 3, 2, 1, 24, 23, 22, // 32530-32539 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 32540-32549 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32550-32559 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 32560-32569 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 32570-32579 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 32580-32589 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 32590-32599 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 32600-32609 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32610-32619 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 32620-32629 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 32630-32639 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 32640-32649 + 3, 2, 1, 34, 33, 32, 31, 30, 29, 28, // 32650-32659 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 32660-32669 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 32670-32679 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 32680-32689 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 32690-32699 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 32700-32709 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 30, // 32710-32719 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 32720-32729 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 32730-32739 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 32740-32749 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 32750-32759 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32760-32769 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 32770-32779 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 32780-32789 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 32790-32799 + 1, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 32800-32809 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 32810-32819 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 32820-32829 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 32830-32839 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 32840-32849 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 32850-32859 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 32860-32869 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 32870-32879 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 32880-32889 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 32890-32899 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 32900-32909 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 32910-32919 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 32920-32929 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 32930-32939 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 32940-32949 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 32950-32959 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 32960-32969 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 32970-32979 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 32980-32989 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 32990-32999 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 33000-33009 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 33010-33019 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 33020-33029 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 33030-33039 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 33040-33049 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 33050-33059 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33060-33069 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 33070-33079 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 33080-33089 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 33090-33099 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 33100-33109 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 30, // 33110-33119 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 33120-33129 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 33130-33139 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 33140-33149 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33150-33159 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 33160-33169 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 33170-33179 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33180-33189 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 33190-33199 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 33200-33209 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 33210-33219 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 33220-33229 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 33230-33239 + 7, 6, 5, 4, 3, 2, 1, 40, 39, 38, // 33240-33249 + 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 33250-33259 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 33260-33269 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 33270-33279 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 33280-33289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33290-33299 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33300-33309 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 33310-33319 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 33320-33329 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 33330-33339 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 33340-33349 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 33350-33359 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 33360-33369 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 33370-33379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33380-33389 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 33390-33399 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 33400-33409 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 33410-33419 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 33420-33429 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 33430-33439 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 33440-33449 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 33450-33459 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 33460-33469 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 33470-33479 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 33480-33489 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 33490-33499 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 33500-33509 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33510-33519 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 33520-33529 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 33530-33539 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 33540-33549 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 33550-33559 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 33560-33569 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 33570-33579 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 33580-33589 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 33590-33599 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 33600-33609 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 33610-33619 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 33620-33629 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 33630-33639 + 1, 6, 5, 4, 3, 2, 1, 32, 31, 30, // 33640-33649 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 33650-33659 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 33660-33669 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 33670-33679 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 33680-33689 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 33690-33699 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 33700-33709 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 33710-33719 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 33720-33729 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 33730-33739 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 33740-33749 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 33750-33759 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 33760-33769 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 33770-33779 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33780-33789 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 33790-33799 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 33800-33809 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 33810-33819 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 33820-33829 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 33830-33839 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33840-33849 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 33850-33859 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 33860-33869 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 33870-33879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 33880-33889 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 33890-33899 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33900-33909 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 33910-33919 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 33920-33929 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 33930-33939 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 33940-33949 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 33950-33959 + 1, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 33960-33969 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 33970-33979 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 33980-33989 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 33990-33999 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 34000-34009 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 34010-34019 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34020-34029 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 34030-34039 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 34040-34049 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 34050-34059 + 1, 62, 61, 60, 59, 58, 57, 56, 55, 54, // 34060-34069 + 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, // 34070-34079 + 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 34080-34089 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 34090-34099 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 34100-34109 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 34110-34119 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 34120-34129 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34130-34139 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 34140-34149 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 34150-34159 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34160-34169 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 34170-34179 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 34180-34189 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 34190-34199 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34200-34209 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 34210-34219 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34220-34229 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 34230-34239 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 34240-34249 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 34250-34259 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 34260-34269 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 34270-34279 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 34280-34289 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 34290-34299 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 34300-34309 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 34310-34319 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 34320-34329 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 34330-34339 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34340-34349 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34350-34359 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 34360-34369 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34370-34379 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 34380-34389 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 34390-34399 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 34400-34409 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34410-34419 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 34420-34429 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 34430-34439 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 34440-34449 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 34450-34459 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 34460-34469 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 34470-34479 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 34480-34489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 34490-34499 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34500-34509 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 34510-34519 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 34520-34529 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 34530-34539 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 34, // 34540-34549 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 34550-34559 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 34560-34569 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 34570-34579 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 34580-34589 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 34590-34599 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 34600-34609 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 34610-34619 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34620-34629 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 34630-34639 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 34640-34649 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 34650-34659 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 34660-34669 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 34670-34679 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 34680-34689 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 34690-34699 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 34700-34709 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34710-34719 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 34720-34729 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 34730-34739 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 34740-34749 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 34750-34759 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 34760-34769 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34770-34779 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 34780-34789 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 34790-34799 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 34800-34809 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 34810-34819 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 34820-34829 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34830-34839 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 22, // 34840-34849 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 34850-34859 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34860-34869 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 34870-34879 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 34880-34889 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 34890-34899 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 34900-34909 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 20, // 34910-34919 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 34920-34929 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 34930-34939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 34940-34949 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34950-34959 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 34960-34969 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 34970-34979 + 1, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 34980-34989 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 34990-34999 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 35000-35009 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 35010-35019 + 3, 2, 1, 4, 3, 2, 1, 24, 23, 22, // 35020-35029 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 35030-35039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35040-35049 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 35050-35059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 35060-35069 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35070-35079 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 35080-35089 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 35090-35099 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 35100-35109 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 35110-35119 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 35120-35129 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35130-35139 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 35140-35149 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 35150-35159 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35160-35169 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 35170-35179 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 35180-35189 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35190-35199 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 35200-35209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35210-35219 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 35220-35229 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 35230-35239 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35240-35249 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 35250-35259 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 35260-35269 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 35270-35279 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35280-35289 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 35290-35299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35300-35309 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 35310-35319 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 35320-35329 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 35330-35339 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 35340-35349 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 35350-35359 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 35360-35369 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35370-35379 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 35380-35389 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 35390-35399 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 35400-35409 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 35410-35419 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 35420-35429 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 35430-35439 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 35440-35449 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35450-35459 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 35460-35469 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 35470-35479 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35480-35489 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 35490-35499 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 35500-35509 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35510-35519 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 35520-35529 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 35530-35539 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 35540-35549 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 35550-35559 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 35560-35569 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 35570-35579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35580-35589 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 35590-35599 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 35600-35609 + 7, 6, 5, 4, 3, 2, 1, 54, 53, 52, // 35610-35619 + 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, // 35620-35629 + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 35630-35639 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 35640-35649 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 35650-35659 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35660-35669 + 1, 6, 5, 4, 3, 2, 1, 52, 51, 50, // 35670-35679 + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, // 35680-35689 + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 35690-35699 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 35700-35709 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 35710-35719 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 35720-35729 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 35730-35739 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 35740-35749 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 35750-35759 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35760-35769 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 35770-35779 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 35780-35789 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 35790-35799 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 35800-35809 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 35810-35819 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35820-35829 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 35830-35839 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35840-35849 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 35850-35859 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 35860-35869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 35870-35879 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 35880-35889 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 35890-35899 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35900-35909 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 35910-35919 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 35920-35929 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 35930-35939 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 35940-35949 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 35950-35959 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 35960-35969 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 35970-35979 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 35980-35989 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 35990-35999 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 36000-36009 + 1, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 36010-36019 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 36020-36029 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 36030-36039 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 36040-36049 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36050-36059 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 36060-36069 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 36070-36079 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 36080-36089 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 36090-36099 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 36100-36109 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 36110-36119 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36120-36129 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 36130-36139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36140-36149 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36150-36159 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 36160-36169 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 36170-36179 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 36180-36189 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 36190-36199 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 36200-36209 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 36210-36219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 36220-36229 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36230-36239 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36240-36249 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36250-36259 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 36260-36269 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 36270-36279 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36280-36289 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 36290-36299 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 36300-36309 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 36310-36319 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 36320-36329 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36330-36339 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 36340-36349 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 36350-36359 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36360-36369 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 36370-36379 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 44, // 36380-36389 + 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 36390-36399 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 36400-36409 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 36410-36419 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36420-36429 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 36430-36439 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36440-36449 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 36450-36459 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 36460-36469 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 36470-36479 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36480-36489 + 3, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 36490-36499 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 36500-36509 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36510-36519 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 36520-36529 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36530-36539 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36540-36549 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 36550-36559 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 36560-36569 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36570-36579 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 36580-36589 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 36590-36599 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 36600-36609 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 36610-36619 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 36620-36629 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 36630-36639 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 36640-36649 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 36650-36659 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36660-36669 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 36670-36679 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 36680-36689 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 36690-36699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 36700-36709 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 36710-36719 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 36720-36729 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 36730-36739 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 36740-36749 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36750-36759 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 36760-36769 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 36770-36779 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 36780-36789 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 36790-36799 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 36800-36809 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36810-36819 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36820-36829 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 36830-36839 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 36840-36849 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 36850-36859 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 36860-36869 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 36870-36879 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 36880-36889 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 36890-36899 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36900-36909 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 36910-36919 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 36920-36929 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36930-36939 + 3, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 36940-36949 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 36950-36959 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 36960-36969 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 36970-36979 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 36980-36989 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 36990-36999 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 37000-37009 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 37010-37019 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 37020-37029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 37030-37039 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 37040-37049 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 37050-37059 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 37060-37069 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37070-37079 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 37080-37089 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 37090-37099 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37100-37109 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 37110-37119 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 37120-37129 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 37130-37139 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 37140-37149 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 37150-37159 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37160-37169 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37170-37179 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 37180-37189 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 37190-37199 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37200-37209 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 37210-37219 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 37220-37229 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 37230-37239 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 37240-37249 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 37250-37259 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 37260-37269 + 3, 2, 1, 4, 3, 2, 1, 30, 29, 28, // 37270-37279 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 37280-37289 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37290-37299 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 37300-37309 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 37310-37319 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37320-37329 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 37330-37339 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37340-37349 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 37350-37359 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 37360-37369 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 37370-37379 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37380-37389 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 37390-37399 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 37400-37409 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 37410-37419 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 37420-37429 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37430-37439 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 37440-37449 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 37450-37459 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 37460-37469 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 37470-37479 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 37480-37489 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 37490-37499 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 37500-37509 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 37510-37519 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 37520-37529 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 37530-37539 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 37540-37549 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37550-37559 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 37560-37569 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 37570-37579 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 37580-37589 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37590-37599 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 37600-37609 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 37610-37619 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 37620-37629 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 37630-37639 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 37640-37649 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 37650-37659 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 37660-37669 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 37670-37679 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37680-37689 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 37690-37699 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37700-37709 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 37710-37719 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 37720-37729 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37730-37739 + 7, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 37740-37749 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 37750-37759 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 37760-37769 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37770-37779 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 37780-37789 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 37790-37799 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37800-37809 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 37810-37819 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37820-37829 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37830-37839 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 37840-37849 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 37850-37859 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37860-37869 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 37870-37879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 37880-37889 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 37890-37899 + 7, 6, 5, 4, 3, 2, 1, 44, 43, 42, // 37900-37909 + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 37910-37919 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 37920-37929 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 37930-37939 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 37940-37949 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 37950-37959 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 37960-37969 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 37970-37979 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 37980-37989 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 37990-37999 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38000-38009 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 38010-38019 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 38020-38029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 38030-38039 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 38040-38049 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 38050-38059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 38060-38069 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38070-38079 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 38080-38089 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 38090-38099 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38100-38109 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 30, // 38110-38119 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 38120-38129 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 38130-38139 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 38140-38149 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 38150-38159 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 38160-38169 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 38170-38179 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 38180-38189 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 38190-38199 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 38200-38209 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 38210-38219 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38220-38229 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 38230-38239 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 38240-38249 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38250-38259 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38260-38269 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 38270-38279 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 38280-38289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 38290-38299 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 38300-38309 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 38310-38319 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 38320-38329 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 38330-38339 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38340-38349 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 38350-38359 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38360-38369 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 38370-38379 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38380-38389 + 3, 2, 1, 38, 37, 36, 35, 34, 33, 32, // 38390-38399 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 38400-38409 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 38410-38419 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38420-38429 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 38430-38439 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 38440-38449 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 38450-38459 + 1, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 38460-38469 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 38470-38479 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 38480-38489 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38490-38499 + 1, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 38500-38509 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 38510-38519 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 38520-38529 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38530-38539 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 38540-38549 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 38550-38559 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 24, // 38560-38569 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 38570-38579 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38580-38589 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 38590-38599 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 38600-38609 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 38610-38619 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 38620-38629 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 38630-38639 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38640-38649 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 38650-38659 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 38660-38669 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 38670-38679 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38680-38689 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 38690-38699 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 38700-38709 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 38710-38719 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 38720-38729 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 38730-38739 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 38740-38749 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 38750-38759 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 38760-38769 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38770-38779 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 38780-38789 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38790-38799 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 38800-38809 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38810-38819 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38820-38829 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 38830-38839 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38840-38849 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38850-38859 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 38860-38869 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 38870-38879 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38880-38889 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38890-38899 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 38900-38909 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 38910-38919 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 38920-38929 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 38930-38939 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38940-38949 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 38950-38959 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 38960-38969 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 38970-38979 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 38980-38989 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 38990-38999 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 39000-39009 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 39010-39019 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 39020-39029 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39030-39039 + 1, 2, 1, 4, 3, 2, 1, 32, 31, 30, // 39040-39049 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 39050-39059 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 39060-39069 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 39070-39079 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 39080-39089 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 39090-39099 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 39100-39109 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 39110-39119 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 39120-39129 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 39130-39139 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 39140-39149 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 39150-39159 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 39160-39169 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39170-39179 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39180-39189 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 39190-39199 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 39200-39209 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 39210-39219 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 39220-39229 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 39230-39239 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39240-39249 + 1, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 39250-39259 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 39260-39269 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 39270-39279 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 39280-39289 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 39290-39299 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 39300-39309 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 39310-39319 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 39320-39329 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39330-39339 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 39340-39349 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 39350-39359 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 39360-39369 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 39370-39379 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 39380-39389 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 39390-39399 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 39400-39409 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 39410-39419 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 39420-39429 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 39430-39439 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 39440-39449 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39450-39459 + 1, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 39460-39469 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 39470-39479 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 39480-39489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 39490-39499 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 39500-39509 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39510-39519 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 39520-39529 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39530-39539 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39540-39549 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 39550-39559 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 39560-39569 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39570-39579 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 39580-39589 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 39590-39599 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 39600-39609 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 39610-39619 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 39620-39629 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 39630-39639 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 39640-39649 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 39650-39659 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 39660-39669 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 39670-39679 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 39680-39689 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 39690-39699 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 39700-39709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 39710-39719 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 39720-39729 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 39730-39739 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 39740-39749 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39750-39759 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 39760-39769 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 39770-39779 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39780-39789 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 39790-39799 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 39800-39809 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39810-39819 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 39820-39829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 39830-39839 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 39840-39849 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 39850-39859 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 39860-39869 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 39870-39879 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 39880-39889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39890-39899 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 39900-39909 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 39910-39919 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 39920-39929 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 39930-39939 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 39940-39949 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 39950-39959 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 39960-39969 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 39970-39979 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 20, // 39980-39989 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 39990-39999 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 40000-40009 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 40010-40019 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 40020-40029 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 24, // 40030-40039 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 40040-40049 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40050-40059 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 40060-40069 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 40070-40079 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 40080-40089 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 40090-40099 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 40100-40109 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40110-40119 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 22, // 40120-40129 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 40130-40139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 40140-40149 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 40150-40159 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 40160-40169 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 40170-40179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 40180-40189 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 40190-40199 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40200-40209 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 40210-40219 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 40220-40229 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 40230-40239 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40240-40249 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 40250-40259 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 40260-40269 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 40270-40279 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 54, // 40280-40289 + 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, // 40290-40299 + 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 40300-40309 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 40310-40319 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 40320-40329 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40330-40339 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 40340-40349 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 40350-40359 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 40360-40369 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 40370-40379 + 7, 6, 5, 4, 3, 2, 1, 36, 35, 34, // 40380-40389 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 40390-40399 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 40400-40409 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40410-40419 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 40420-40429 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 40430-40439 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 40440-40449 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 40450-40459 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 40460-40469 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40470-40479 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 40480-40489 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 40490-40499 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 40500-40509 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 40510-40519 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 40520-40529 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40530-40539 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 40540-40549 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 40550-40559 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 40560-40569 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 40570-40579 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 40580-40589 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 40590-40599 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 40600-40609 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 40610-40619 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 40620-40629 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 54, // 40630-40639 + 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, // 40640-40649 + 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 40650-40659 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 40660-40669 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 40670-40679 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40680-40689 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 40690-40699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 30, // 40700-40709 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 40710-40719 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 40720-40729 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 40730-40739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 40740-40749 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 40750-40759 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 40760-40769 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 40770-40779 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 40780-40789 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 40790-40799 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40800-40809 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 40810-40819 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 40820-40829 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 40830-40839 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 40840-40849 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 40850-40859 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 40860-40869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 40870-40879 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 40880-40889 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 40890-40899 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 40900-40909 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 40910-40919 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 40920-40929 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 40930-40939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 40940-40949 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 40950-40959 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40960-40969 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 40970-40979 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 40980-40989 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 40990-40999 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41000-41009 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 41010-41019 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 41020-41029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 41030-41039 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 41040-41049 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 41050-41059 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 41060-41069 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 41070-41079 + 1, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 41080-41089 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 41090-41099 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 41100-41109 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 41110-41119 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41120-41129 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41130-41139 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 41140-41149 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41150-41159 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 41160-41169 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 41170-41179 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 41180-41189 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41190-41199 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 41200-41209 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 41210-41219 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 41220-41229 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 41230-41239 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 41240-41249 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 41250-41259 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 41260-41269 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41270-41279 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 41280-41289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 34, // 41290-41299 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 41300-41309 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 41310-41319 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 41320-41329 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 41330-41339 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41340-41349 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 41350-41359 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 41360-41369 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41370-41379 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 41380-41389 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 41390-41399 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41400-41409 + 1, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 41410-41419 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 41420-41429 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 41430-41439 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 41440-41449 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 41450-41459 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 41460-41469 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 41470-41479 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41480-41489 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 41490-41499 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 41500-41509 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 41510-41519 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 41520-41529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 41530-41539 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 30, // 41540-41549 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 41550-41559 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 41560-41569 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 41570-41579 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 41580-41589 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 41590-41599 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 41600-41609 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 41610-41619 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 41620-41629 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41630-41639 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 41640-41649 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 41650-41659 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 41660-41669 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41670-41679 + 1, 6, 5, 4, 3, 2, 1, 32, 31, 30, // 41680-41689 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 41690-41699 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 41700-41709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 41710-41719 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 41720-41729 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 41730-41739 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 41740-41749 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 41750-41759 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41760-41769 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 41770-41779 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 41780-41789 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41790-41799 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 41800-41809 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 41810-41819 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 41820-41829 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 41830-41839 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 41840-41849 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 41850-41859 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 41860-41869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 41870-41879 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 41880-41889 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 41890-41899 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 41900-41909 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 41910-41919 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 41920-41929 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41930-41939 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 41940-41949 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 41950-41959 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 41960-41969 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 41970-41979 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 41980-41989 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 41990-41999 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 42000-42009 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 42010-42019 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 42020-42029 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 42030-42039 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 42040-42049 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42050-42059 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42060-42069 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 42070-42079 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 42080-42089 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42090-42099 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 42100-42109 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 42110-42119 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42120-42129 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 42130-42139 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 42140-42149 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 42150-42159 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 42160-42169 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 42170-42179 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 42180-42189 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 42190-42199 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 42200-42209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42210-42219 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 42220-42229 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 42230-42239 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 42240-42249 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 42250-42259 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 42260-42269 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42270-42279 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 42280-42289 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 42290-42299 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 42300-42309 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 42310-42319 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 42320-42329 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 42330-42339 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 42340-42349 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 42350-42359 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 42360-42369 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 42370-42379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42380-42389 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 42390-42399 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 24, // 42400-42409 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 42410-42419 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 42420-42429 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 42430-42439 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 42440-42449 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 42450-42459 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 42460-42469 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 42470-42479 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 42480-42489 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 42490-42499 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 42500-42509 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 42510-42519 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 42520-42529 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 42530-42539 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 42540-42549 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 42550-42559 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 42560-42569 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 42570-42579 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 42580-42589 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 42590-42599 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42600-42609 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 42610-42619 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 42620-42629 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42630-42639 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 42640-42649 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 42650-42659 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 42660-42669 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 42670-42679 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 42680-42689 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 42690-42699 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 42700-42709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 42710-42719 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 42720-42729 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 42730-42739 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 42740-42749 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 42750-42759 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 42760-42769 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 42770-42779 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 42780-42789 + 3, 2, 1, 4, 3, 2, 1, 24, 23, 22, // 42790-42799 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 42800-42809 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 42810-42819 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 42820-42829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 42830-42839 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 42840-42849 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 42850-42859 + 3, 2, 1, 36, 35, 34, 33, 32, 31, 30, // 42860-42869 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 42870-42879 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 42880-42889 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 42890-42899 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 42900-42909 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 42910-42919 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 42920-42929 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 42930-42939 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 42940-42949 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 42950-42959 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 42960-42969 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 42970-42979 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 42980-42989 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 42990-42999 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 43000-43009 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 43010-43019 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 43020-43029 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 43030-43039 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 43040-43049 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43050-43059 + 3, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 43060-43069 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 43070-43079 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43080-43089 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 43090-43099 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 43100-43109 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 43110-43119 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43120-43129 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 43130-43139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43140-43149 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 43150-43159 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 43160-43169 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 43170-43179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 43180-43189 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43190-43199 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 43200-43209 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43210-43219 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 43220-43229 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 43230-43239 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 43240-43249 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43250-43259 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43260-43269 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43270-43279 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 43280-43289 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 43290-43299 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43300-43309 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 43310-43319 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43320-43329 + 1, 60, 59, 58, 57, 56, 55, 54, 53, 52, // 43330-43339 + 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, // 43340-43349 + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 43350-43359 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 43360-43369 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 43370-43379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43380-43389 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 43390-43399 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 43400-43409 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 43410-43419 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 43420-43429 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43430-43439 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43440-43449 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 43450-43459 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 43460-43469 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43470-43479 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 43480-43489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 43490-43499 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 43500-43509 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 43510-43519 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 43520-43529 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43530-43539 + 1, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 43540-43549 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 43550-43559 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43560-43569 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 43570-43579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43580-43589 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 43590-43599 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 43600-43609 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 43610-43619 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 43620-43629 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 43630-43639 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 43640-43649 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43650-43659 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 43660-43669 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 43670-43679 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43680-43689 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 43690-43699 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43700-43709 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 43710-43719 + 1, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 43720-43729 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 43730-43739 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43740-43749 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 43750-43759 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 43760-43769 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 43770-43779 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 43780-43789 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 43790-43799 + 1, 52, 51, 50, 49, 48, 47, 46, 45, 44, // 43800-43809 + 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 43810-43819 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 43820-43829 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 43830-43839 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43840-43849 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 43850-43859 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 43860-43869 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 43870-43879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 43880-43889 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 43890-43899 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43900-43909 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 43910-43919 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 43920-43929 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 43930-43939 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 43940-43949 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 43950-43959 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 43960-43969 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 43970-43979 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 43980-43989 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 43990-43999 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 44000-44009 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 44010-44019 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 44020-44029 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44030-44039 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 44040-44049 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 44050-44059 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44060-44069 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 44070-44079 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 44080-44089 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44090-44099 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44100-44109 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 44110-44119 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 44120-44129 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 44130-44139 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 44140-44149 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 44150-44159 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44160-44169 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 44170-44179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 44180-44189 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44190-44199 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 44200-44209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44210-44219 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 44220-44229 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 44230-44239 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 44240-44249 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 44250-44259 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 44260-44269 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 44270-44279 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 44280-44289 + 3, 2, 1, 58, 57, 56, 55, 54, 53, 52, // 44290-44299 + 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, // 44300-44309 + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 44310-44319 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 44320-44329 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 44330-44339 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44340-44349 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 44350-44359 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44360-44369 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44370-44379 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 28, // 44380-44389 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 44390-44399 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 44400-44409 + 7, 6, 5, 4, 3, 2, 1, 32, 31, 30, // 44410-44419 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 44420-44429 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 44430-44439 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 44440-44449 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 44450-44459 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 44460-44469 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 44470-44479 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 44480-44489 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 44490-44499 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 44500-44509 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 44510-44519 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44520-44529 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 44530-44539 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 44540-44549 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 44550-44559 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 44560-44569 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 44570-44579 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 44580-44589 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 44590-44599 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 44600-44609 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 44610-44619 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 44620-44629 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 44630-44639 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 44640-44649 + 1, 6, 5, 4, 3, 2, 1, 26, 25, 24, // 44650-44659 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 44660-44669 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 44670-44679 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 44680-44689 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 44690-44699 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44700-44709 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 44710-44719 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 44720-44729 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44730-44739 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 44740-44749 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 44750-44759 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 44760-44769 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 44770-44779 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 44780-44789 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 44790-44799 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 44800-44809 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 44810-44819 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 44820-44829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 44830-44839 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 44840-44849 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 44850-44859 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 44860-44869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 44870-44879 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 44880-44889 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 44890-44899 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 44900-44909 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 44910-44919 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 44920-44929 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 44930-44939 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 44940-44949 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 44950-44959 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 44960-44969 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 44970-44979 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 44980-44989 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 44990-44999 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 45000-45009 + 3, 2, 1, 40, 39, 38, 37, 36, 35, 34, // 45010-45019 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 45020-45029 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 45030-45039 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 45040-45049 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 45050-45059 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 45060-45069 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 45070-45079 + 3, 2, 1, 36, 35, 34, 33, 32, 31, 30, // 45080-45089 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 45090-45099 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 45100-45109 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 45110-45119 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 45120-45129 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 45130-45139 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 45140-45149 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45150-45159 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 45160-45169 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 45170-45179 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45180-45189 + 1, 6, 5, 4, 3, 2, 1, 36, 35, 34, // 45190-45199 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 45200-45209 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 45210-45219 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 45220-45229 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 45230-45239 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 45240-45249 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 45250-45259 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 45260-45269 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45270-45279 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 45280-45289 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 45290-45299 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 45300-45309 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 45310-45319 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 45320-45329 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 45330-45339 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 45340-45349 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45350-45359 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 45360-45369 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 45370-45379 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 45380-45389 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 45390-45399 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 45400-45409 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 45410-45419 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 45420-45429 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 42, // 45430-45439 + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 45440-45449 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 45450-45459 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 45460-45469 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45470-45479 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45480-45489 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 45490-45499 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 45500-45509 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 45510-45519 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 45520-45529 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 45530-45539 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 45540-45549 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 45550-45559 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 45560-45569 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 45570-45579 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 45580-45589 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 45590-45599 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 45600-45609 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 45610-45619 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45620-45629 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45630-45639 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 45640-45649 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 45650-45659 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 45660-45669 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 45670-45679 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45680-45689 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 45690-45699 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 45700-45709 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 45710-45719 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 45720-45729 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 45730-45739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45740-45749 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 45750-45759 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 45760-45769 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 38, // 45770-45779 + 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 45780-45789 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 45790-45799 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 45800-45809 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 45810-45819 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 45820-45829 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 45830-45839 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 45840-45849 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 45850-45859 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 45860-45869 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 45870-45879 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 45880-45889 + 3, 2, 1, 50, 49, 48, 47, 46, 45, 44, // 45890-45899 + 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 45900-45909 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 45910-45919 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 45920-45929 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 45930-45939 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 45940-45949 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 45950-45959 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 45960-45969 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 45970-45979 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 32, // 45980-45989 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 45990-45999 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 46000-46009 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46010-46019 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 46020-46029 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 46030-46039 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 46040-46049 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46050-46059 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46060-46069 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 46070-46079 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46080-46089 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 46090-46099 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 46100-46109 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 46110-46119 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46120-46129 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 46130-46139 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 46140-46149 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 46150-46159 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46160-46169 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46170-46179 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 46180-46189 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 46190-46199 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 46200-46209 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 46210-46219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 46220-46229 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 46230-46239 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 46240-46249 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46250-46259 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46260-46269 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 46270-46279 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 46280-46289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46290-46299 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 46300-46309 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 46310-46319 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 46320-46329 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 46330-46339 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 46340-46349 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 46350-46359 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 46360-46369 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46370-46379 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 46380-46389 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 46390-46399 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46400-46409 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 46410-46419 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 46420-46429 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 46430-46439 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 46440-46449 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 46450-46459 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46460-46469 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 46470-46479 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 46480-46489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 46490-46499 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 46500-46509 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46510-46519 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 46520-46529 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 46530-46539 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 46540-46549 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 46550-46559 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 46560-46569 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 46570-46579 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 46580-46589 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46590-46599 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 46600-46609 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 46610-46619 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46620-46629 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 46630-46639 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 46640-46649 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46650-46659 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 46660-46669 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 46670-46679 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 46680-46689 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46690-46699 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 46700-46709 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46710-46719 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 46720-46729 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 46730-46739 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 46740-46749 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 46750-46759 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 46760-46769 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 46770-46779 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 46780-46789 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 46790-46799 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 46800-46809 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 46810-46819 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 46820-46829 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 46830-46839 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46840-46849 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 46850-46859 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 46860-46869 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 46870-46879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 46880-46889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 46890-46899 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 46900-46909 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 46910-46919 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46920-46929 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 46930-46939 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 46940-46949 + 7, 6, 5, 4, 3, 2, 1, 36, 35, 34, // 46950-46959 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 46960-46969 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 46970-46979 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 46980-46989 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 46990-46999 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 47000-47009 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 47010-47019 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 47020-47029 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47030-47039 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47040-47049 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 28, // 47050-47059 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 47060-47069 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 47070-47079 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 47080-47089 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 47090-47099 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47100-47109 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 47110-47119 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 47120-47129 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 47130-47139 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 47140-47149 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47150-47159 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 47160-47169 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 47170-47179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 47180-47189 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 47190-47199 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 47200-47209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47210-47219 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 47220-47229 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 47230-47239 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47240-47249 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 47250-47259 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 47260-47269 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 47270-47279 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 47280-47289 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 47290-47299 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 47300-47309 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 47310-47319 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 47320-47329 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 47330-47339 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47340-47349 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 47350-47359 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 47360-47369 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47370-47379 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 47380-47389 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 47390-47399 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 47400-47409 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 47410-47419 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47420-47429 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47430-47439 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 47440-47449 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 32, // 47450-47459 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 47460-47469 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 47470-47479 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47480-47489 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 47490-47499 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 47500-47509 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 47510-47519 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 47520-47529 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 47530-47539 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 47540-47549 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 47550-47559 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 47560-47569 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47570-47579 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47580-47589 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 47590-47599 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 47600-47609 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 47610-47619 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 47620-47629 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 47630-47639 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 47640-47649 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 22, // 47650-47659 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 47660-47669 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47670-47679 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 47680-47689 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 47690-47699 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47700-47709 + 1, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 47710-47719 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 47720-47729 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 47730-47739 + 1, 2, 1, 34, 33, 32, 31, 30, 29, 28, // 47740-47749 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 47750-47759 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 47760-47769 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 47770-47779 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47780-47789 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 47790-47799 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 47800-47809 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 47810-47819 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 47820-47829 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 47830-47839 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 47840-47849 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 47850-47859 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 47860-47869 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 47870-47879 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 47880-47889 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 47890-47899 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 47900-47909 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 47910-47919 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 47920-47929 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 47930-47939 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 47940-47949 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 47950-47959 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 47960-47969 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 47970-47979 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 47980-47989 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 47990-47999 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 48000-48009 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 48010-48019 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 20, // 48020-48029 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 48030-48039 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 48040-48049 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 48050-48059 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48060-48069 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 48070-48079 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48080-48089 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 48090-48099 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 48100-48109 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 48110-48119 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48120-48129 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 48130-48139 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 48140-48149 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 48150-48159 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 48160-48169 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 48170-48179 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 48180-48189 + 3, 2, 1, 4, 3, 2, 1, 24, 23, 22, // 48190-48199 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 48200-48209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48210-48219 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 48220-48229 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 48230-48239 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 48240-48249 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 48250-48259 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48260-48269 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48270-48279 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 48280-48289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 48290-48299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48300-48309 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 48310-48319 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 48320-48329 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 48330-48339 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48340-48349 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 48350-48359 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48360-48369 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48370-48379 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 48380-48389 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 48390-48399 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 48400-48409 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 48410-48419 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 48420-48429 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 48430-48439 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 48440-48449 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48450-48459 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 48460-48469 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 48470-48479 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 48480-48489 + 1, 6, 5, 4, 3, 2, 1, 26, 25, 24, // 48490-48499 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 48500-48509 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48510-48519 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 48520-48529 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 48530-48539 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 48540-48549 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48550-48559 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 48560-48569 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 48570-48579 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 48580-48589 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 48590-48599 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48600-48609 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 48610-48619 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 48620-48629 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 48630-48639 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 48640-48649 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48650-48659 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48660-48669 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 52, // 48670-48679 + 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, // 48680-48689 + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 48690-48699 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 48700-48709 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 48710-48719 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48720-48729 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 48730-48739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 48740-48749 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 48750-48759 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 48760-48769 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 48770-48779 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 48780-48789 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 48790-48799 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 48800-48809 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 48810-48819 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 48820-48829 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 48830-48839 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 48840-48849 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 48850-48859 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 48860-48869 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48870-48879 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 48880-48889 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 48890-48899 + 7, 6, 5, 4, 3, 2, 1, 40, 39, 38, // 48900-48909 + 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 48910-48919 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 48920-48929 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 48930-48939 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 48940-48949 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 48950-48959 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48960-48969 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 48970-48979 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 48980-48989 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 48990-48999 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 49000-49009 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 49010-49019 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49020-49029 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 49030-49039 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 49040-49049 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 49050-49059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 49060-49069 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49070-49079 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 49080-49089 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49090-49099 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 49100-49109 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 49110-49119 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 49120-49129 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 49130-49139 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 49140-49149 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 49150-49159 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 49160-49169 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 49170-49179 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49180-49189 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 49190-49199 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 49200-49209 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49210-49219 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 49220-49229 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 49230-49239 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49240-49249 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 49250-49259 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 49260-49269 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 49270-49279 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 49280-49289 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 49290-49299 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 49300-49309 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 49310-49319 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49320-49329 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 24, // 49330-49339 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 49340-49349 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49350-49359 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 22, // 49360-49369 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 49370-49379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49380-49389 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 49390-49399 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 49400-49409 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 49410-49419 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 49420-49429 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 49430-49439 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49440-49449 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 49450-49459 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 49460-49469 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 49470-49479 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 49480-49489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 49490-49499 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 49500-49509 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49510-49519 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 49520-49529 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 49530-49539 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 49540-49549 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 38, // 49550-49559 + 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 49560-49569 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 49570-49579 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 49580-49589 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 49590-49599 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 49600-49609 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 49610-49619 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 49620-49629 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 24, // 49630-49639 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 49640-49649 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49650-49659 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 49660-49669 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49670-49679 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 49680-49689 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 49690-49699 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49700-49709 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 49710-49719 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 49720-49729 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 49730-49739 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 49740-49749 + 7, 6, 5, 4, 3, 2, 1, 26, 25, 24, // 49750-49759 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 49760-49769 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49770-49779 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 49780-49789 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49790-49799 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 49800-49809 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49810-49819 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 49820-49829 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 49830-49839 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 49840-49849 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 49850-49859 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49860-49869 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 49870-49879 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49880-49889 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 49890-49899 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 49900-49909 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 49910-49919 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 49920-49929 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 49930-49939 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 49940-49949 + 7, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 49950-49959 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 49960-49969 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 49970-49979 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 49980-49989 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 49990-49999 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 50000-50009 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50010-50019 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 50020-50029 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 50030-50039 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 50040-50049 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 50050-50059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 50060-50069 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 50070-50079 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 50080-50089 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 50090-50099 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50100-50109 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 50110-50119 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 50120-50129 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 50130-50139 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 50140-50149 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 50150-50159 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 50160-50169 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 50170-50179 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 50180-50189 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 50190-50199 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 50200-50209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50210-50219 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 50220-50229 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 50230-50239 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 50240-50249 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50250-50259 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 50260-50269 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 50270-50279 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 50280-50289 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 50290-50299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50300-50309 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50310-50319 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 50320-50329 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 50330-50339 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 50340-50349 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 50350-50359 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 50360-50369 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 50370-50379 + 3, 2, 1, 4, 3, 2, 1, 24, 23, 22, // 50380-50389 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 50390-50399 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50400-50409 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 50410-50419 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 50420-50429 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50430-50439 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 50440-50449 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 50450-50459 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 50460-50469 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 50470-50479 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 50480-50489 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 50490-50499 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 50500-50509 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 50510-50519 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 50520-50529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 50530-50539 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 50540-50549 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 50550-50559 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 50560-50569 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50570-50579 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 50580-50589 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 28, // 50590-50599 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 50600-50609 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 50610-50619 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 50620-50629 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 50630-50639 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 50640-50649 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 50650-50659 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50660-50669 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 50670-50679 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 50680-50689 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 50690-50699 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 50700-50709 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 50710-50719 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 50720-50729 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50730-50739 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 50740-50749 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 50750-50759 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 50760-50769 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 50770-50779 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 32, // 50780-50789 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 50790-50799 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 50800-50809 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50810-50819 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 50820-50829 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 50830-50839 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 50840-50849 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 50850-50859 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 50860-50869 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 50870-50879 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50880-50889 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 50890-50899 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 50900-50909 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 50910-50919 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 50920-50929 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 50930-50939 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 50940-50949 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 50950-50959 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 50960-50969 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 50970-50979 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 50980-50989 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 50990-50999 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 51000-51009 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 51010-51019 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51020-51029 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51030-51039 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 51040-51049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 51050-51059 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51060-51069 + 1, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 51070-51079 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 51080-51089 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 51090-51099 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 51100-51109 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 51110-51119 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51120-51129 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 51130-51139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51140-51149 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 51150-51159 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 51160-51169 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 51170-51179 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51180-51189 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 51190-51199 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 51200-51209 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 51210-51219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 51220-51229 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 51230-51239 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 51240-51249 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 51250-51259 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 51260-51269 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51270-51279 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 51280-51289 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 51290-51299 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 51300-51309 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 51310-51319 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 51320-51329 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51330-51339 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 12, // 51340-51349 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51350-51359 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 51360-51369 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51370-51379 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 51380-51389 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 51390-51399 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 51400-51409 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 51410-51419 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 51420-51429 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 51430-51439 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 51440-51449 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51450-51459 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51460-51469 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 51470-51479 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 51480-51489 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51490-51499 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 51500-51509 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 51510-51519 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 51520-51529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 51530-51539 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51540-51549 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51550-51559 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 51560-51569 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 51570-51579 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51580-51589 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 51590-51599 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 51600-51609 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 51610-51619 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51620-51629 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 51630-51639 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 51640-51649 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 51650-51659 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51660-51669 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 51670-51679 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 51680-51689 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 51690-51699 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51700-51709 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 51710-51719 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 51720-51729 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 51730-51739 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 51740-51749 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 51750-51759 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 51760-51769 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 51770-51779 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 51780-51789 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 51790-51799 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 51800-51809 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 51810-51819 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 51820-51829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 51830-51839 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51840-51849 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 51850-51859 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 51860-51869 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 51870-51879 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 51880-51889 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 51890-51899 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 51900-51909 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 51910-51919 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 51920-51929 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51930-51939 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 51940-51949 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 51950-51959 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51960-51969 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 51970-51979 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 51980-51989 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 51990-51999 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 52000-52009 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52010-52019 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 52020-52029 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 52030-52039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52040-52049 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 52050-52059 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 52060-52069 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52070-52079 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 52080-52089 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 52090-52099 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 52100-52109 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52110-52119 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 52120-52129 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 52130-52139 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 52140-52149 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 52150-52159 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 52160-52169 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 52170-52179 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 52180-52189 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52190-52199 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 52200-52209 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 52210-52219 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 52220-52229 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 52230-52239 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 52240-52249 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 52250-52259 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 52260-52269 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 52270-52279 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 52280-52289 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52290-52299 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 52300-52309 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 52310-52319 + 1, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 52320-52329 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 52330-52339 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 52340-52349 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52350-52359 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 52360-52369 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 52370-52379 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 52380-52389 + 1, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 52390-52399 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 52400-52409 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 52410-52419 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 52420-52429 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 52430-52439 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 52440-52449 + 3, 2, 1, 4, 3, 2, 1, 32, 31, 30, // 52450-52459 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 52460-52469 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 52470-52479 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 52480-52489 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52490-52499 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52500-52509 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 52510-52519 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 52520-52529 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52530-52539 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 52540-52549 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 52550-52559 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 52560-52569 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 52570-52579 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 52580-52589 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 52590-52599 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 52600-52609 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 52610-52619 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 52620-52629 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 28, // 52630-52639 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 52640-52649 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 52650-52659 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 52660-52669 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 52670-52679 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52680-52689 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 52690-52699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 52700-52709 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52710-52719 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 52720-52729 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 52730-52739 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 52740-52749 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 52750-52759 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 52760-52769 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 52770-52779 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 52780-52789 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 52790-52799 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 52800-52809 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 52810-52819 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 52820-52829 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 52830-52839 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 52840-52849 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 52850-52859 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 52860-52869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 52870-52879 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 52880-52889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52890-52899 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 52900-52909 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 52910-52919 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 52920-52929 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 52930-52939 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 52940-52949 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 52950-52959 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 52960-52969 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 52970-52979 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 52980-52989 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 52990-52999 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 53000-53009 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 53010-53019 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 53020-53029 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 53030-53039 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 53040-53049 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 53050-53059 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 53060-53069 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 53070-53079 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 53080-53089 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 53090-53099 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 53100-53109 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 53110-53119 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 53120-53129 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 53130-53139 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 53140-53149 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53150-53159 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53160-53169 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 53170-53179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 53180-53189 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 53190-53199 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 53200-53209 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 53210-53219 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53220-53229 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 28, // 53230-53239 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 53240-53249 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 53250-53259 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 53260-53269 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 53270-53279 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 53280-53289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 53290-53299 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 53300-53309 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 53310-53319 + 3, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 53320-53329 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 53330-53339 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 53340-53349 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 53350-53359 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 53360-53369 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 53370-53379 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 53380-53389 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53390-53399 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 53400-53409 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 53410-53419 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 53420-53429 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 53430-53439 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 53440-53449 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 53450-53459 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 53460-53469 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 53470-53479 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 53480-53489 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 53490-53499 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 53500-53509 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 53510-53519 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 53520-53529 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 53530-53539 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 53540-53549 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 53550-53559 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 53560-53569 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 53570-53579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53580-53589 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 53590-53599 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 53600-53609 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 53610-53619 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 53620-53629 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 53630-53639 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 53640-53649 + 3, 2, 1, 4, 3, 2, 1, 24, 23, 22, // 53650-53659 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 53660-53669 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53670-53679 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 53680-53689 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 53690-53699 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 53700-53709 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 53710-53719 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53720-53729 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 53730-53739 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 53740-53749 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 53750-53759 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 53760-53769 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 53770-53779 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 53780-53789 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 53790-53799 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 53800-53809 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 53810-53819 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53820-53829 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 53830-53839 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 53840-53849 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 53850-53859 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 53860-53869 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53870-53879 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 53880-53889 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 53890-53899 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 53900-53909 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 53910-53919 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 53920-53929 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 53930-53939 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 53940-53949 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 28, // 53950-53959 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 53960-53969 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 53970-53979 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 53980-53989 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 53990-53999 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54000-54009 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 54010-54019 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 54020-54029 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 54030-54039 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 54040-54049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 54050-54059 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 54060-54069 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 54070-54079 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 54080-54089 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54090-54099 + 1, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 54100-54109 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54110-54119 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 54120-54129 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 54130-54139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54140-54149 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 54150-54159 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 54160-54169 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54170-54179 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 54180-54189 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 54190-54199 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 54200-54209 + 7, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 54210-54219 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 54220-54229 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 54230-54239 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54240-54249 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 54250-54259 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 54260-54269 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 54270-54279 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 54280-54289 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 54290-54299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54300-54309 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 54310-54319 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 54320-54329 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 54330-54339 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 54340-54349 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54350-54359 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 54360-54369 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 54370-54379 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 54380-54389 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54390-54399 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 54400-54409 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 54410-54419 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 54420-54429 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 54430-54439 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 20, // 54440-54449 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 54450-54459 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 54460-54469 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 54470-54479 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 54480-54489 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 54490-54499 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 54500-54509 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 54510-54519 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 54520-54529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 54530-54539 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 54540-54549 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 54550-54559 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 54560-54569 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 54570-54579 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 54580-54589 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54590-54599 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 54600-54609 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 54610-54619 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 54620-54629 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 54630-54639 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 54640-54649 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 54650-54659 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 54660-54669 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 30, // 54670-54679 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 54680-54689 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 54690-54699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 54700-54709 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 54710-54719 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 54720-54729 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 54730-54739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54740-54749 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 54750-54759 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 54760-54769 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 54770-54779 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 54780-54789 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 30, // 54790-54799 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 54800-54809 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 54810-54819 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 54820-54829 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 54830-54839 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54840-54849 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 54850-54859 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 54860-54869 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 54870-54879 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 54880-54889 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 54890-54899 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 54900-54909 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 54910-54919 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 54920-54929 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54930-54939 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 54940-54949 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 54950-54959 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 54960-54969 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 54970-54979 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 54980-54989 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 54990-54999 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 55000-55009 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55010-55019 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 55020-55029 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 55030-55039 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 55040-55049 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 55050-55059 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 55060-55069 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 24, // 55070-55079 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 55080-55089 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 55090-55099 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 55100-55109 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 55110-55119 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 55120-55129 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 55130-55139 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 55140-55149 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 55150-55159 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 55160-55169 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 55170-55179 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 55180-55189 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55190-55199 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 55200-55209 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 55210-55219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 55220-55229 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 55230-55239 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 55240-55249 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 32, // 55250-55259 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 55260-55269 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 55270-55279 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55280-55289 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 55290-55299 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 55300-55309 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 55310-55319 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55320-55329 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 55330-55339 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 55340-55349 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 55350-55359 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 55360-55369 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 55370-55379 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 55380-55389 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 55390-55399 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55400-55409 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 55410-55419 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 55420-55429 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 55430-55439 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 55440-55449 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 55450-55459 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 55460-55469 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 55470-55479 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 55480-55489 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55490-55499 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55500-55509 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 55510-55519 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 55520-55529 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55530-55539 + 1, 6, 5, 4, 3, 2, 1, 32, 31, 30, // 55540-55549 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 55550-55559 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 55560-55569 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 55570-55579 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 55580-55589 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 55590-55599 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 55600-55609 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 55610-55619 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55620-55629 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 55630-55639 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 55640-55649 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55650-55659 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 55660-55669 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 55670-55679 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55680-55689 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 55690-55699 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55700-55709 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 55710-55719 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 55720-55729 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 55730-55739 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 55740-55749 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 55750-55759 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 55760-55769 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 55770-55779 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 55780-55789 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 55790-55799 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 55800-55809 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 55810-55819 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 55820-55829 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 55830-55839 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 55840-55849 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 55850-55859 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55860-55869 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 55870-55879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 55880-55889 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 55890-55899 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 55900-55909 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 55910-55919 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 55920-55929 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 55930-55939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 55940-55949 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 55950-55959 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 55960-55969 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 55970-55979 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 55980-55989 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 55990-55999 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 30, // 56000-56009 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 56010-56019 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 56020-56029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 56030-56039 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 56040-56049 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 56050-56059 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 56060-56069 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56070-56079 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 56080-56089 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 56090-56099 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 56100-56109 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 56110-56119 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 56120-56129 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 56130-56139 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 56140-56149 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 56150-56159 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 56160-56169 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 56170-56179 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 56180-56189 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 56190-56199 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 28, // 56200-56209 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 56210-56219 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 56220-56229 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 56230-56239 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 56240-56249 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 56250-56259 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 30, // 56260-56269 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 56270-56279 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 56280-56289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 56290-56299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56300-56309 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 56310-56319 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 56320-56329 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 56330-56339 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 56340-56349 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 56350-56359 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 56360-56369 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 56370-56379 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 56380-56389 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 56390-56399 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 56400-56409 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 56410-56419 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56420-56429 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 56430-56439 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 56440-56449 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 56450-56459 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 56460-56469 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 56470-56479 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 56480-56489 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56490-56499 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 56500-56509 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 56510-56519 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 56520-56529 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 56530-56539 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 56540-56549 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 56550-56559 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 56560-56569 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 56570-56579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56580-56589 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 56590-56599 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56600-56609 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 56610-56619 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 56620-56629 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 56630-56639 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 56640-56649 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 56650-56659 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 56660-56669 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56670-56679 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 56680-56689 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56690-56699 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56700-56709 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 56710-56719 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56720-56729 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 56730-56739 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 56740-56749 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 56750-56759 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 56760-56769 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 56770-56779 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 56780-56789 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 56790-56799 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 56800-56809 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 56810-56819 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 56820-56829 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 56830-56839 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 56840-56849 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 56850-56859 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 56860-56869 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 56870-56879 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56880-56889 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 56890-56899 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 56900-56909 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56910-56919 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 56920-56929 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56930-56939 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 56940-56949 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 56950-56959 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 56960-56969 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 56970-56979 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 56980-56989 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 38, // 56990-56999 + 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 57000-57009 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 57010-57019 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57020-57029 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 57030-57039 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 57040-57049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 57050-57059 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 57060-57069 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 57070-57079 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 57080-57089 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 57090-57099 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 57100-57109 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 57110-57119 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57120-57129 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 57130-57139 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 57140-57149 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 57150-57159 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 57160-57169 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 57170-57179 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57180-57189 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 57190-57199 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 57200-57209 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57210-57219 + 1, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 57220-57229 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57230-57239 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57240-57249 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 57250-57259 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 57260-57269 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 57270-57279 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 57280-57289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57290-57299 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 57300-57309 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 57310-57319 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 57320-57329 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57330-57339 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 57340-57349 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57350-57359 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 57360-57369 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 57370-57379 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 57380-57389 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 57390-57399 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 57400-57409 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 57410-57419 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 57420-57429 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 57430-57439 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57440-57449 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 57450-57459 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 57460-57469 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57470-57479 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 57480-57489 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 57490-57499 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 57500-57509 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57510-57519 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 28, // 57520-57529 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 57530-57539 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57540-57549 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 57550-57559 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57560-57569 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57570-57579 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 57580-57589 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 57590-57599 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 57600-57609 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 57610-57619 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57620-57629 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 57630-57639 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 57640-57649 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 57650-57659 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 57660-57669 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 57670-57679 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 57680-57689 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 57690-57699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 57700-57709 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 57710-57719 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 57720-57729 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 57730-57739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57740-57749 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 57750-57759 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 57760-57769 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 57770-57779 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 57780-57789 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 57790-57799 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 20, // 57800-57809 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 57810-57819 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 57820-57829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 57830-57839 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 57840-57849 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 57850-57859 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 57860-57869 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57870-57879 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 57880-57889 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 57890-57899 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 57900-57909 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 57910-57919 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 57920-57929 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 57930-57939 + 3, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 57940-57949 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 57950-57959 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 57960-57969 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 57970-57979 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 57980-57989 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 57990-57999 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 58000-58009 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 58010-58019 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 58020-58029 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 58030-58039 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 58040-58049 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 58050-58059 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 58060-58069 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 58070-58079 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 58080-58089 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 58090-58099 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 58100-58109 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 58110-58119 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 58120-58129 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58130-58139 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 58140-58149 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 58150-58159 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 58160-58169 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 58170-58179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 58180-58189 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 58190-58199 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 58200-58209 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 58210-58219 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 58220-58229 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 58230-58239 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 58240-58249 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 58250-58259 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 58260-58269 + 1, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 58270-58279 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 58280-58289 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 58290-58299 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 58300-58309 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 58310-58319 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58320-58329 + 7, 6, 5, 4, 3, 2, 1, 26, 25, 24, // 58330-58339 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 58340-58349 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 58350-58359 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 10, // 58360-58369 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 58370-58379 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 58380-58389 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 58390-58399 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 58400-58409 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 58410-58419 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 58420-58429 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 58430-58439 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 58440-58449 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 58450-58459 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58460-58469 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 58470-58479 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 58480-58489 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 58490-58499 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 58500-58509 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 58510-58519 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58520-58529 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 58530-58539 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 58540-58549 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58550-58559 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 58560-58569 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 58570-58579 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 58580-58589 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 58590-58599 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 58600-58609 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 58610-58619 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 58620-58629 + 1, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 58630-58639 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58640-58649 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 58650-58659 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 58660-58669 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 58670-58679 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 58680-58689 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 58690-58699 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 58700-58709 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58710-58719 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 58720-58729 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 58730-58739 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58740-58749 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 58750-58759 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 58760-58769 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58770-58779 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 42, // 58780-58789 + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 58790-58799 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 58800-58809 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 58810-58819 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 58820-58829 + 1, 58, 57, 56, 55, 54, 53, 52, 51, 50, // 58830-58839 + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, // 58840-58849 + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 58850-58859 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 58860-58869 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 58870-58879 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 58880-58889 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 58890-58899 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 58900-58909 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 58910-58919 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 58920-58929 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 58930-58939 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 58940-58949 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 58950-58959 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 58960-58969 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 58970-58979 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 58980-58989 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 58990-58999 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 59000-59009 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59010-59019 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 59020-59029 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 59030-59039 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59040-59049 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 59050-59059 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 59060-59069 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 59070-59079 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 59080-59089 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 59090-59099 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 59100-59109 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 59110-59119 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 59120-59129 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59130-59139 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 59140-59149 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 59150-59159 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 59160-59169 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 59170-59179 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 59180-59189 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 59190-59199 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 59200-59209 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 59210-59219 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 59220-59229 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 59230-59239 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 59240-59249 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 59250-59259 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 59260-59269 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 59270-59279 + 1, 52, 51, 50, 49, 48, 47, 46, 45, 44, // 59280-59289 + 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, // 59290-59299 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 59300-59309 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 59310-59319 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 59320-59329 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 59330-59339 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59340-59349 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 59350-59359 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 59360-59369 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 59370-59379 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 59380-59389 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 59390-59399 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 59400-59409 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 59410-59419 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 59420-59429 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59430-59439 + 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 59440-59449 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 59450-59459 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 59460-59469 + 1, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 59470-59479 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 59480-59489 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 59490-59499 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 59500-59509 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 59510-59519 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 59520-59529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 59530-59539 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 59540-59549 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 59550-59559 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 59560-59569 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59570-59579 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 59580-59589 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 59590-59599 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59600-59609 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 59610-59619 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 22, // 59620-59629 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 59630-59639 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59640-59649 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 59650-59659 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 59660-59669 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 59670-59679 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 59680-59689 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 59690-59699 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 59700-59709 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 59710-59719 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 59720-59729 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 59730-59739 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 59740-59749 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 59750-59759 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59760-59769 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 59770-59779 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59780-59789 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 59790-59799 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 59800-59809 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 59810-59819 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 59820-59829 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 59830-59839 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 59840-59849 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 59850-59859 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 59860-59869 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 59870-59879 + 7, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 59880-59889 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 59890-59899 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 59900-59909 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59910-59919 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 59920-59929 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 59930-59939 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59940-59949 + 1, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 59950-59959 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59960-59969 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 59970-59979 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 59980-59989 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 59990-59999 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 60000-60009 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 60010-60019 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 60020-60029 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 60030-60039 + 1, 36, 35, 34, 33, 32, 31, 30, 29, 28, // 60040-60049 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 60050-60059 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 60060-60069 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 60070-60079 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 60080-60089 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60090-60099 + 1, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 60100-60109 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 60110-60119 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 60120-60129 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 60130-60139 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 60140-60149 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60150-60159 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 40, // 60160-60169 + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 60170-60179 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 60180-60189 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 60190-60199 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 60200-60209 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 60210-60219 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 60220-60229 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 60230-60239 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60240-60249 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 60250-60259 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60260-60269 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 60270-60279 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 60280-60289 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 60290-60299 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 60300-60309 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 60310-60319 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60320-60329 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 60330-60339 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 60340-60349 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 60350-60359 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 60360-60369 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 60370-60379 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 60380-60389 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 60390-60399 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 60400-60409 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 60410-60419 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 60420-60429 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 60430-60439 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 60440-60449 + 7, 6, 5, 4, 3, 2, 1, 36, 35, 34, // 60450-60459 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 60460-60469 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 60470-60479 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 60480-60489 + 3, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 60490-60499 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 60500-60509 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60510-60519 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 60520-60529 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 50, // 60530-60539 + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, // 60540-60549 + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 60550-60559 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 60560-60569 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 60570-60579 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 60580-60589 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60590-60599 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 60600-60609 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 60610-60619 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 60620-60629 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 60630-60639 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 60640-60649 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 60650-60659 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 60660-60669 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 60670-60679 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 60680-60689 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 60690-60699 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 60700-60709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 60710-60719 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 60720-60729 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 60730-60739 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 60740-60749 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 60750-60759 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 60760-60769 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 60770-60779 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 60780-60789 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 60790-60799 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60800-60809 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60810-60819 + 1, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 60820-60829 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 60830-60839 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 60840-60849 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 60850-60859 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 60860-60869 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 60870-60879 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 60880-60889 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 60890-60899 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 60900-60909 + 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, // 60910-60919 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 60920-60929 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 60930-60939 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 60940-60949 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 60950-60959 + 1, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 60960-60969 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 60970-60979 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 60980-60989 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 60990-60999 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 61000-61009 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 61010-61019 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 61020-61029 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61030-61039 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 61040-61049 + 1, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 61050-61059 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 61060-61069 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 61070-61079 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61080-61089 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 22, // 61090-61099 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 61100-61109 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61110-61119 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 61120-61129 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61130-61139 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61140-61149 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 61150-61159 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 42, // 61160-61169 + 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, // 61170-61179 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 61180-61189 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 61190-61199 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61200-61209 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61210-61219 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 61220-61229 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 61230-61239 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61240-61249 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 61250-61259 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 61260-61269 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61270-61279 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 61280-61289 + 1, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 61290-61299 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 61300-61309 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 61310-61319 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61320-61329 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 61330-61339 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 61340-61349 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 61350-61359 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 61360-61369 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 61370-61379 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 61380-61389 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61390-61399 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, // 61400-61409 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 61410-61419 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 61420-61429 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61430-61439 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 61440-61449 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61450-61459 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 61460-61469 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61470-61479 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 61480-61489 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 61490-61499 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 61500-61509 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 24, // 61510-61519 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 61520-61529 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61530-61539 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 61540-61549 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, // 61550-61559 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 61560-61569 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61570-61579 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 61580-61589 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61590-61599 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 61600-61609 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 61610-61619 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 61620-61629 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 61630-61639 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 61640-61649 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 61650-61659 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 61660-61669 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 61670-61679 + 1, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 61680-61689 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61690-61699 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 61700-61709 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 61710-61719 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 61720-61729 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 61730-61739 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61740-61749 + 1, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 61750-61759 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 61760-61769 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61770-61779 + 1, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 61780-61789 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 61790-61799 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61800-61809 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 61810-61819 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 61820-61829 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 61830-61839 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 61840-61849 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61850-61859 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61860-61869 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 30, // 61870-61879 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 61880-61889 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 61890-61899 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 61900-61909 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 61910-61919 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 61920-61929 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 61930-61939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 61940-61949 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 61950-61959 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 61960-61969 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 61970-61979 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 61980-61989 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 61990-61999 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 62000-62009 + 1, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 62010-62019 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 62020-62029 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 62030-62039 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 62040-62049 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 62050-62059 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62060-62069 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62070-62079 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 62080-62089 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 62090-62099 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 62100-62109 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 62110-62119 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 62120-62129 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 62130-62139 + 1, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 62140-62149 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 62150-62159 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62160-62169 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 62170-62179 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 62180-62189 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62190-62199 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 62200-62209 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 62210-62219 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62220-62229 + 3, 2, 1, 40, 39, 38, 37, 36, 35, 34, // 62230-62239 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 62240-62249 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 62250-62259 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62260-62269 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 62270-62279 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 62280-62289 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 62290-62299 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 62300-62309 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62310-62319 + 3, 2, 1, 4, 3, 2, 1, 20, 19, 18, // 62320-62329 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 62330-62339 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 62340-62349 + 1, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 62350-62359 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 62360-62369 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62370-62379 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 62380-62389 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62390-62399 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 62400-62409 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 62410-62419 + 3, 2, 1, 36, 35, 34, 33, 32, 31, 30, // 62420-62429 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 62430-62439 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 62440-62449 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 62450-62459 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 62460-62469 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 62470-62479 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 62480-62489 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 62490-62499 + 1, 6, 5, 4, 3, 2, 1, 26, 25, 24, // 62500-62509 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 62510-62519 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62520-62529 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 62530-62539 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 62540-62549 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62550-62559 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 62560-62569 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62570-62579 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62580-62589 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 62590-62599 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 62600-62609 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 62610-62619 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 62620-62629 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 62630-62639 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62640-62649 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 24, // 62650-62659 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 62660-62669 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62670-62679 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 62680-62689 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62690-62699 + 1, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 62700-62709 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62710-62719 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 62720-62729 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62730-62739 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 62740-62749 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 62750-62759 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 62760-62769 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 62770-62779 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62780-62789 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62790-62799 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 62800-62809 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 62810-62819 + 7, 6, 5, 4, 3, 2, 1, 24, 23, 22, // 62820-62829 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 62830-62839 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62840-62849 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62850-62859 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 62860-62869 + 3, 2, 1, 24, 23, 22, 21, 20, 19, 18, // 62870-62879 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 62880-62889 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 62890-62899 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 62900-62909 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62910-62919 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 62920-62929 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 30, // 62930-62939 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 62940-62949 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 62950-62959 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 62960-62969 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 62970-62979 + 1, 2, 1, 4, 3, 2, 1, 2, 1, 40, // 62980-62989 + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, // 62990-62999 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 63000-63009 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 63010-63019 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 63020-63029 + 1, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 63030-63039 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 63040-63049 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 63050-63059 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 63060-63069 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 18, // 63070-63079 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 63080-63089 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 63090-63099 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 63100-63109 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 63110-63119 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 63120-63129 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 63130-63139 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 30, // 63140-63149 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 63150-63159 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 63160-63169 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 63170-63179 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 63180-63189 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 63190-63199 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 63200-63209 + 1, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 63210-63219 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 63220-63229 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 63230-63239 + 1, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 63240-63249 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 63250-63259 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 63260-63269 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 63270-63279 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 63280-63289 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 12, // 63290-63299 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 63300-63309 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 63310-63319 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 63320-63329 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 63330-63339 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 63340-63349 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 63350-63359 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 63360-63369 + 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 63370-63379 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 63380-63389 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 63390-63399 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 63400-63409 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 63410-63419 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 63420-63429 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 63430-63439 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 63440-63449 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 63450-63459 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 63460-63469 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 63470-63479 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 63480-63489 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 22, // 63490-63499 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 63500-63509 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 63510-63519 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 63520-63529 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 63530-63539 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 63540-63549 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 63550-63559 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 63560-63569 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 63570-63579 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 63580-63589 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 63590-63599 + 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 63600-63609 + 1, 6, 5, 4, 3, 2, 1, 12, 11, 10, // 63610-63619 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 63620-63629 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 63630-63639 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 10, // 63640-63649 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 63650-63659 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 63660-63669 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 63670-63679 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 63680-63689 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 63690-63699 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 63700-63709 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 63710-63719 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 63720-63729 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 63730-63739 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 63740-63749 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 63750-63759 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 63760-63769 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 63770-63779 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 63780-63789 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 63790-63799 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 63800-63809 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 63810-63819 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 63820-63829 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 63830-63839 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 63840-63849 + 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, // 63850-63859 + 3, 2, 1, 38, 37, 36, 35, 34, 33, 32, // 63860-63869 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 63870-63879 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 63880-63889 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 63890-63899 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 63900-63909 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 63910-63919 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 20, // 63920-63929 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 63930-63939 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 28, // 63940-63949 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 63950-63959 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 63960-63969 + 7, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 63970-63979 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 63980-63989 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 63990-63999 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 64000-64009 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 14, // 64010-64019 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64020-64029 + 3, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 64030-64039 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 64040-64049 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64050-64059 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 64060-64069 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64070-64079 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64080-64089 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 64090-64099 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 64100-64109 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64110-64119 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 64120-64129 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 64130-64139 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64140-64149 + 1, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 64150-64159 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64160-64169 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 64170-64179 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 28, // 64180-64189 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 64190-64199 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 64200-64209 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 64210-64219 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 64220-64229 + 1, 6, 5, 4, 3, 2, 1, 34, 33, 32, // 64230-64239 + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, // 64240-64249 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 64250-64259 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64260-64269 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 64270-64279 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 64280-64289 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64290-64299 + 1, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 64300-64309 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 64310-64319 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 64320-64329 + 3, 2, 1, 40, 39, 38, 37, 36, 35, 34, // 64330-64339 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 64340-64349 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 64350-64359 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64360-64369 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 64370-64379 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 64380-64389 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 64390-64399 + 3, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 64400-64409 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 64410-64419 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64420-64429 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 64430-64439 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64440-64449 + 1, 2, 1, 30, 29, 28, 27, 26, 25, 24, // 64450-64459 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 64460-64469 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64470-64479 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 10, // 64480-64489 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 64490-64499 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64500-64509 + 3, 2, 1, 40, 39, 38, 37, 36, 35, 34, // 64510-64519 + 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, // 64520-64529 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 64530-64539 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64540-64549 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 64550-64559 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 64560-64569 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 64570-64579 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64580-64589 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64590-64599 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 64600-64609 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 64610-64619 + 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 64620-64629 + 3, 2, 1, 28, 27, 26, 25, 24, 23, 22, // 64630-64639 + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, // 64640-64649 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64650-64659 + 1, 2, 1, 4, 3, 2, 1, 12, 11, 10, // 64660-64669 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 64670-64679 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64680-64689 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 64690-64699 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 8, // 64700-64709 + 7, 6, 5, 4, 3, 2, 1, 30, 29, 28, // 64710-64719 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 64720-64729 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 64730-64739 + 7, 6, 5, 4, 3, 2, 1, 16, 15, 14, // 64740-64749 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 64750-64759 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 64760-64769 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64770-64779 + 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 64780-64789 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 64790-64799 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64800-64809 + 1, 6, 5, 4, 3, 2, 1, 32, 31, 30, // 64810-64819 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 64820-64829 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 64830-64839 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 64840-64849 + 3, 2, 1, 18, 17, 16, 15, 14, 13, 12, // 64850-64859 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64860-64869 + 1, 6, 5, 4, 3, 2, 1, 2, 1, 12, // 64870-64879 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64880-64889 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64890-64899 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 64900-64909 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 64910-64919 + 1, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 64920-64929 + 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, // 64930-64939 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 64940-64949 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 64950-64959 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 28, // 64960-64969 + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, // 64970-64979 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 64980-64989 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 64990-64999 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 65000-65009 + 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 65010-65019 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, // 65020-65029 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 65030-65039 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 65040-65049 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 65050-65059 + 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, // 65060-65069 + 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 65070-65079 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, // 65080-65089 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 65090-65099 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 65100-65109 + 1, 8, 7, 6, 5, 4, 3, 2, 1, 4, // 65110-65119 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 12, // 65120-65129 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 65130-65139 + 1, 6, 5, 4, 3, 2, 1, 20, 19, 18, // 65140-65149 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 65150-65159 + 7, 6, 5, 4, 3, 2, 1, 4, 3, 2, // 65160-65169 + 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 65170-65179 + 3, 2, 1, 20, 19, 18, 17, 16, 15, 14, // 65180-65189 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 65190-65199 + 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, // 65200-65209 + 3, 2, 1, 26, 25, 24, 23, 22, 21, 20, // 65210-65219 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 65220-65229 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 65230-65239 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 65240-65249 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 65250-65259 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 18, // 65260-65269 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 65270-65279 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 65280-65289 + 3, 2, 1, 16, 15, 14, 13, 12, 11, 10, // 65290-65299 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, // 65300-65309 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 65310-65319 + 3, 2, 1, 4, 3, 2, 1, 26, 25, 24, // 65320-65329 + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, // 65330-65339 + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 65340-65349 + 3, 2, 1, 4, 3, 2, 1, 14, 13, 12, // 65350-65359 + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 65360-65369 + 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, // 65370-65379 + 1, 12, 11, 10, 9, 8, 7, 6, 5, 4, // 65380-65389 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 65390-65399 + 7, 6, 5, 4, 3, 2, 1, 6, 5, 4, // 65400-65409 + 3, 2, 1, 6, 5, 4, 3, 2, 1, 4, // 65410-65419 + 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, // 65420-65429 + 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, // 65430-65439 + 7, 6, 5, 4, 3, 2, 1, 2, 1, 30, // 65440-65449 + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, // 65450-65459 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 65460-65469 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 18, // 65470-65479 + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, // 65480-65489 + 7, 6, 5, 4, 3, 2, 1, 22, 21, 20, // 65490-65499 + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, // 65500-65509 + 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, // 65510-65519 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 65520-65529 + 0, 0, 0, 0, 0, 0, + } + + lohi [256]struct{ lo, hi int } +) + +func init() { + for i, v := range liars { + blk := v >> 24 + x := &lohi[blk] + if x.lo == 0 || i < x.lo { + x.lo = i + } + if i > x.hi { + x.hi = i + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/mathutil/test_deps.go b/Godeps/_workspace/src/github.com/cznic/mathutil/test_deps.go new file mode 100644 index 000000000..40054dcad --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/mathutil/test_deps.go @@ -0,0 +1,11 @@ +// Copyright (c) 2014 The mathutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mathutil + +// Pull test dependencies too. +// Enables easy 'go test X' after 'go get X' +import ( +// nothing yet +) diff --git a/Godeps/_workspace/src/github.com/cznic/ql/AUTHORS b/Godeps/_workspace/src/github.com/cznic/ql/AUTHORS new file mode 100644 index 000000000..0078f5f5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/AUTHORS @@ -0,0 +1,11 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/ql/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/ql/CONTRIBUTORS new file mode 100644 index 000000000..6b6aed2dd --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/CONTRIBUTORS @@ -0,0 +1,12 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Dan Kortschak +Jan Mercl <0xjnml@gmail.com> +OpenNota +Viktor Kojouharov diff --git a/Godeps/_workspace/src/github.com/cznic/ql/LICENSE b/Godeps/_workspace/src/github.com/cznic/ql/LICENSE new file mode 100644 index 000000000..0d10c02b9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The ql Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/ql/Makefile b/Godeps/_workspace/src/github.com/cznic/ql/Makefile new file mode 100644 index 000000000..72dc67d43 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/Makefile @@ -0,0 +1,74 @@ +# Copyright (c) 2014 The ql Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all clean nuke + +all: editor scanner.go parser.go + go build + go vet || true + golint + make todo + go install ./... + +bench: all + go test -run NONE -bench . + +clean: + go clean + rm -f *~ y.go y.tab.c *.out ql.test + +coerce.go: helper.go + if [ -f coerce.go ] ; then rm coerce.go ; fi + go run helper.go | gofmt > $@ + +cover: + t=$(shell tempfile) ; go test -coverprofile $$t && go tool cover -html $$t && unlink $$t + +cpu: ql.test + go test -c + ./$< -test.bench . -test.cpuprofile cpu.out + go tool pprof --lines $< cpu.out + +editor: ql.y scanner.go parser.go coerce.go + gofmt -s -l -w *.go + go test -i + go test + +internalError: + egrep -ho '"internal error.*"' *.go | sort | cat -n + +mem: ql.test + go test -c + ./$< -test.bench . -test.memprofile mem.out + go tool pprof --lines --web --alloc_space $< mem.out + +nuke: + go clean -i + +parser.go: parser.y + a=$(shell tempfile) ; \ + goyacc -o /dev/null -xegen $$a $< ; \ + goyacc -cr -o $@ -xe $$a $< ; \ + rm -f $$a + sed -i -e 's|//line.*||' -e 's/yyEofCode/yyEOFCode/' $@ + +ql.test: all + +ql.y: doc.go + sed -n '1,/^package/ s/^\/\/ //p' < $< \ + | ebnf2y -o $@ -oe $*.ebnf -start StatementList -pkg $* -p _ + goyacc -cr -o /dev/null $@ + +scanner.go: scanner.l parser.go + golex -o $@ $< + +todo: + @grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* *.go *.l parser.y || true + @grep -n TODO *.go *.l parser.y testdata.ql || true + @grep -n BUG *.go *.l parser.y || true + @grep -n println *.go *.l parser.y || true + +later: + @grep -n LATER *.go *.l parser.y || true + @grep -n MAYBE *.go *.l parser.y || true diff --git a/Godeps/_workspace/src/github.com/cznic/ql/README.md b/Godeps/_workspace/src/github.com/cznic/ql/README.md new file mode 100644 index 000000000..c7a453a32 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/README.md @@ -0,0 +1,21 @@ +ql +== + +Package ql is a pure Go embedded (S)QL database. + +Installation + + $ go get github.com/cznic/ql + +Documentation: [godoc.org/github.com/cznic/ql](http://godoc.org/github.com/cznic/ql) + +---- + +Accompanying tool to play with a DB + +Installation + + $ go get github.com/cznic/ql/ql + +Documentation: [godoc.org/github.com/cznic/ql/ql](http://godoc.org/github.com/cznic/ql/ql) + diff --git a/Godeps/_workspace/src/github.com/cznic/ql/all_test.go b/Godeps/_workspace/src/github.com/cznic/ql/all_test.go new file mode 100644 index 000000000..a095860c5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/all_test.go @@ -0,0 +1,3148 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "bytes" + "crypto/md5" + "database/sql" + "fmt" + "io" + "io/ioutil" + "log" + "math/big" + "math/rand" + "os" + "path" + "path/filepath" + "runtime" + "runtime/debug" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/cznic/strutil" +) + +// Note: All benchmarks report MB/s equal to record/s. +const benchScale = 1e6 + +func init() { + log.SetFlags(log.Flags() | log.Lshortfile) + isTesting = true +} + +func dieHard(exitValue int) { + debug.PrintStack() + os.Exit(exitValue) +} + +func dbg(s string, va ...interface{}) { + if s == "" { + s = strings.Repeat("%v ", len(va)) + } + _, fn, fl, _ := runtime.Caller(1) + fmt.Printf("dbg %s:%d: ", path.Base(fn), fl) + fmt.Printf(s, va...) + fmt.Println() +} + +func caller(s string, va ...interface{}) { + if s == "" { + s = strings.Repeat("%v ", len(va)) + } + _, fn, fl, _ := runtime.Caller(2) + fmt.Printf("caller: %s:%d: ", path.Base(fn), fl) + fmt.Printf(s, va...) + fmt.Println() + _, fn, fl, _ = runtime.Caller(1) + fmt.Printf("\tcallee: %s:%d: ", path.Base(fn), fl) + fmt.Println() +} + +func use(...interface{}) {} + +func dumpTables3(r *root) { + fmt.Printf("---- r.head %d, r.thead %p\n", r.head, r.thead) + for k, v := range r.tables { + fmt.Printf("%p: %s->%+v\n", v, k, v) + } + fmt.Println("") +} + +func dumpTables2(s storage) { + fmt.Println("****") + h := int64(1) + for h != 0 { + d, err := s.Read(nil, h) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%d: %v\n", h, d) + h = d[0].(int64) + } + fmt.Println("") +} + +func (db *DB) dumpTables() string { + var buf bytes.Buffer + for k, v := range db.root.tables { + buf.WriteString(fmt.Sprintf("%s->%v, %v\n", k, v.h, v.next)) + } + return buf.String() +} + +func fldsString(f []*fld) string { + a := []string{} + for _, v := range f { + a = append(a, v.name) + } + return strings.Join(a, " ") +} + +type testDB interface { + setup() (db *DB, err error) + mark() (err error) + teardown(ctx *TCtx) (err error) +} + +var ( + _ testDB = (*fileTestDB)(nil) + _ testDB = (*memTestDB)(nil) +) + +type memTestDB struct { + db *DB + m0 int64 +} + +func (m *memTestDB) setup() (db *DB, err error) { + if m.db, err = OpenMem(); err != nil { + return + } + + return m.db, nil +} + +func (m *memTestDB) mark() (err error) { + m.m0, err = m.db.store.Verify() + if err != nil { + m.m0 = -1 + } + return +} + +func (m *memTestDB) teardown(ctx *TCtx) (err error) { + if m.m0 < 0 { + return + } + + n, err := m.db.store.Verify() + if err != nil { + return + } + + if g, e := n, m.m0; g != e { + return fmt.Errorf("STORAGE LEAK: allocs: got %d, exp %d", g, e) + } + + if ctx == nil { + return nil + } + + _, _, err = m.db.Execute(ctx, txCommit) + return err +} + +type fileTestDB struct { + db *DB + gmp0 int + m0 int64 +} + +func (m *fileTestDB) setup() (db *DB, err error) { + m.gmp0 = runtime.GOMAXPROCS(0) + f, err := ioutil.TempFile("", "ql-test-") + if err != nil { + return + } + + if m.db, err = OpenFile(f.Name(), &Options{}); err != nil { + return + } + + return m.db, nil +} + +func (m *fileTestDB) mark() (err error) { + m.m0, err = m.db.store.Verify() + if err != nil { + m.m0 = -1 + } + return +} + +func (m *fileTestDB) teardown(ctx *TCtx) (err error) { + runtime.GOMAXPROCS(m.gmp0) + defer func() { + f := m.db.store.(*file) + errSet(&err, m.db.Close()) + os.Remove(f.f0.Name()) + if f.wal != nil { + os.Remove(f.wal.Name()) + } + }() + + if m.m0 < 0 { + return + } + + n, err := m.db.store.Verify() + if err != nil { + return + } + + if g, e := n, m.m0; g != e { + return fmt.Errorf("STORAGE LEAK: allocs: got %d, exp %d", g, e) + } + + if ctx == nil { + return nil + } + + _, _, err = m.db.Execute(ctx, txCommit) + return err +} + +type osFileTestDB struct { + db *DB + gmp0 int + m0 int64 +} + +func (m *osFileTestDB) setup() (db *DB, err error) { + m.gmp0 = runtime.GOMAXPROCS(0) + f, err := ioutil.TempFile("", "ql-test-osfile") + if err != nil { + return + } + + if m.db, err = OpenFile("", &Options{OSFile: f}); err != nil { + return + } + + return m.db, nil +} + +func (m *osFileTestDB) mark() (err error) { + m.m0, err = m.db.store.Verify() + if err != nil { + m.m0 = -1 + } + return +} + +func (m *osFileTestDB) teardown(ctx *TCtx) (err error) { + runtime.GOMAXPROCS(m.gmp0) + defer func() { + f := m.db.store.(*file) + errSet(&err, m.db.Close()) + os.Remove(f.f0.Name()) + if f.wal != nil { + os.Remove(f.wal.Name()) + } + }() + + if m.m0 < 0 { + return + } + + n, err := m.db.store.Verify() + if err != nil { + return + } + + if g, e := n, m.m0; g != e { + return fmt.Errorf("STORAGE LEAK: allocs: got %d, exp %d", g, e) + } + + if ctx == nil { + return nil + } + + _, _, err = m.db.Execute(ctx, txCommit) + return err +} + +func TestMemStorage(t *testing.T) { + test(t, &memTestDB{}) +} + +func TestFileStorage(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + test(t, &fileTestDB{}) +} + +func TestOSFileStorage(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + test(t, &osFileTestDB{}) +} + +var ( + compiledCommit = MustCompile("COMMIT;") + compiledCreate = MustCompile("BEGIN TRANSACTION; CREATE TABLE t (i16 int16, s16 string, s string);") + compiledCreate2 = MustCompile("BEGIN TRANSACTION; CREATE TABLE t (i16 int16, s16 string, s string); COMMIT;") + compiledIns = MustCompile("INSERT INTO t VALUES($1, $2, $3);") + compiledSelect = MustCompile("SELECT * FROM t;") + compiledSelectOrderBy = MustCompile("SELECT * FROM t ORDER BY i16, s16;") + compiledTrunc = MustCompile("BEGIN TRANSACTION; TRUNCATE TABLE t; COMMIT;") +) + +func rnds16(rng *rand.Rand, n int) string { + a := make([]string, n) + for i := range a { + a[i] = fmt.Sprintf("%016x", rng.Int63()) + } + return strings.Join(a, "") +} + +var ( + benchmarkScaleOnce sync.Once + benchmarkSelectOnce = map[string]sync.Once{} +) + +func benchProlog(b *testing.B) { + benchmarkScaleOnce.Do(func() { + b.Logf(` +============================================================= +NOTE: All benchmarks report records/s as %d bytes/s. +=============================================================`, int64(benchScale)) + }) +} + +func benchmarkSelect(b *testing.B, n int, sel List, ts testDB) { + if testing.Verbose() { + benchProlog(b) + id := fmt.Sprintf("%T|%d", ts, n) + once := benchmarkSelectOnce[id] + once.Do(func() { + b.Logf(`Having a table of %d records, each of size 1kB, measure the performance of +%s +`, n, sel) + }) + benchmarkSelectOnce[id] = once + } + + db, err := ts.setup() + if err != nil { + b.Error(err) + return + } + + defer ts.teardown(nil) + + ctx := NewRWCtx() + if _, i, err := db.Execute(ctx, compiledCreate); err != nil { + b.Error(i, err) + return + } + + rng := rand.New(rand.NewSource(42)) + for i := 0; i < n; i++ { + if _, _, err := db.Execute(ctx, compiledIns, int16(rng.Int()), rnds16(rng, 1), rnds16(rng, 63)); err != nil { + b.Error(err) + return + } + } + + if _, i, err := db.Execute(ctx, compiledCommit); err != nil { + b.Error(i, err) + return + } + + b.SetBytes(int64(n) * benchScale) + runtime.GC() + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, index, err := db.Execute(nil, sel) + if err != nil { + b.Error(index, err) + return + } + + if err = rs[0].Do(false, func(record []interface{}) (bool, error) { return true, nil }); err != nil { + b.Errorf("%v %T(%#v)", err, err, err) + return + } + } + b.StopTimer() + +} + +func BenchmarkSelectMem1kBx1e2(b *testing.B) { + benchmarkSelect(b, 1e2, compiledSelect, &memTestDB{}) +} + +func BenchmarkSelectMem1kBx1e3(b *testing.B) { + benchmarkSelect(b, 1e3, compiledSelect, &memTestDB{}) +} + +func BenchmarkSelectMem1kBx1e4(b *testing.B) { + benchmarkSelect(b, 1e4, compiledSelect, &memTestDB{}) +} + +func BenchmarkSelectMem1kBx1e5(b *testing.B) { + benchmarkSelect(b, 1e5, compiledSelect, &memTestDB{}) +} + +func BenchmarkSelectFile1kBx1e2(b *testing.B) { + benchmarkSelect(b, 1e2, compiledSelect, &fileTestDB{}) +} + +func BenchmarkSelectFile1kBx1e3(b *testing.B) { + benchmarkSelect(b, 1e3, compiledSelect, &fileTestDB{}) +} + +func BenchmarkSelectFile1kBx1e4(b *testing.B) { + benchmarkSelect(b, 1e4, compiledSelect, &fileTestDB{}) +} + +func BenchmarkSelectFile1kBx1e5(b *testing.B) { + benchmarkSelect(b, 1e5, compiledSelect, &fileTestDB{}) +} + +func BenchmarkSelectOrderedMem1kBx1e2(b *testing.B) { + benchmarkSelect(b, 1e2, compiledSelectOrderBy, &memTestDB{}) +} + +func BenchmarkSelectOrderedMem1kBx1e3(b *testing.B) { + benchmarkSelect(b, 1e3, compiledSelectOrderBy, &memTestDB{}) +} + +func BenchmarkSelectOrderedMem1kBx1e4(b *testing.B) { + benchmarkSelect(b, 1e4, compiledSelectOrderBy, &memTestDB{}) +} + +func BenchmarkSelectOrderedFile1kBx1e2(b *testing.B) { + benchmarkSelect(b, 1e2, compiledSelectOrderBy, &fileTestDB{}) +} + +func BenchmarkSelectOrderedFile1kBx1e3(b *testing.B) { + benchmarkSelect(b, 1e3, compiledSelectOrderBy, &fileTestDB{}) +} + +func BenchmarkSelectOrderedFile1kBx1e4(b *testing.B) { + benchmarkSelect(b, 1e4, compiledSelectOrderBy, &fileTestDB{}) +} + +func TestString(t *testing.T) { + for _, v := range testdata { + a := strings.Split(v, "\n|") + v = a[0] + v = strings.Replace(v, "∨", "|", -1) + v = strings.Replace(v, "⩖", "||", -1) + l, err := Compile(v) + if err != nil { + continue + } + + if s := l.String(); len(s) == 0 { + t.Fatal("List.String() returned empty string") + } + } +} + +var benchmarkInsertOnce = map[string]sync.Once{} + +func benchmarkInsert(b *testing.B, batch, total int, ts testDB) { + if testing.Verbose() { + benchProlog(b) + id := fmt.Sprintf("%T|%d|%d", ts, batch, total) + once := benchmarkInsertOnce[id] + once.Do(func() { + b.Logf(`In batches of %d record(s), insert a total of %d records, each of size 1kB, into a table. + +`, batch, total) + }) + benchmarkInsertOnce[id] = once + } + + if total%batch != 0 { + b.Fatal("internal error 001") + } + + db, err := ts.setup() + if err != nil { + b.Error(err) + return + } + + defer ts.teardown(nil) + + ctx := NewRWCtx() + if _, i, err := db.Execute(ctx, compiledCreate2); err != nil { + b.Error(i, err) + return + } + + s := fmt.Sprintf(`(0, "0123456789abcdef", "%s"),`, rnds16(rand.New(rand.NewSource(42)), 63)) + var buf bytes.Buffer + buf.WriteString("BEGIN TRANSACTION; INSERT INTO t VALUES\n") + for i := 0; i < batch; i++ { + buf.WriteString(s) + } + buf.WriteString("; COMMIT;") + ins, err := Compile(buf.String()) + if err != nil { + b.Error(err) + return + } + + b.SetBytes(int64(total) * benchScale) + runtime.GC() + b.ResetTimer() + for i := 0; i < b.N; i++ { + for n := 0; n != total; n += batch { + if _, _, err = db.Execute(ctx, ins); err != nil { + b.Error(err) + return + } + } + b.StopTimer() + if _, _, err = db.Execute(ctx, compiledTrunc); err != nil { + b.Error(err) + } + b.StartTimer() + } + b.StopTimer() +} + +func BenchmarkInsertMem1kBn1e0t1e2(b *testing.B) { + benchmarkInsert(b, 1e0, 1e2, &memTestDB{}) +} + +func BenchmarkInsertFile1kBn1e0t1e2(b *testing.B) { + benchmarkInsert(b, 1e0, 1e2, &fileTestDB{}) +} + +//============================================================================= + +func BenchmarkInsertMem1kBn1e1t1e2(b *testing.B) { + benchmarkInsert(b, 1e1, 1e2, &memTestDB{}) +} + +func BenchmarkInsertFile1kBn1e1t1e2(b *testing.B) { + benchmarkInsert(b, 1e1, 1e2, &fileTestDB{}) +} + +func BenchmarkInsertMem1kBn1e1t1e3(b *testing.B) { + benchmarkInsert(b, 1e1, 1e3, &memTestDB{}) +} + +func BenchmarkInsertFile1kBn1e1t1e3(b *testing.B) { + benchmarkInsert(b, 1e1, 1e3, &fileTestDB{}) +} + +//============================================================================= + +func BenchmarkInsertMem1kBn1e2t1e2(b *testing.B) { + benchmarkInsert(b, 1e2, 1e2, &memTestDB{}) +} + +func BenchmarkInsertFile1kBn1e2t1e2(b *testing.B) { + benchmarkInsert(b, 1e2, 1e2, &fileTestDB{}) +} + +func BenchmarkInsertMem1kBn1e2t1e3(b *testing.B) { + benchmarkInsert(b, 1e2, 1e3, &memTestDB{}) +} + +func BenchmarkInsertFile1kBn1e2t1e3(b *testing.B) { + benchmarkInsert(b, 1e2, 1e3, &fileTestDB{}) +} + +func BenchmarkInsertMem1kBn1e2t1e4(b *testing.B) { + benchmarkInsert(b, 1e2, 1e4, &memTestDB{}) +} + +func BenchmarkInsertFile1kBn1e2t1e4(b *testing.B) { + benchmarkInsert(b, 1e2, 1e4, &fileTestDB{}) +} + +//============================================================================= + +func BenchmarkInsertMem1kBn1e3t1e3(b *testing.B) { + benchmarkInsert(b, 1e3, 1e3, &memTestDB{}) +} + +func BenchmarkInsertFile1kBn1e3t1e3(b *testing.B) { + benchmarkInsert(b, 1e3, 1e3, &fileTestDB{}) +} + +func BenchmarkInsertMem1kBn1e3t1e4(b *testing.B) { + benchmarkInsert(b, 1e3, 1e4, &memTestDB{}) +} + +func BenchmarkInsertFile1kBn1e3t1e4(b *testing.B) { + benchmarkInsert(b, 1e3, 1e4, &fileTestDB{}) +} + +func BenchmarkInsertMem1kBn1e3t1e5(b *testing.B) { + benchmarkInsert(b, 1e3, 1e5, &memTestDB{}) +} + +func BenchmarkInsertFile1kBn1e3t1e5(b *testing.B) { + benchmarkInsert(b, 1e3, 1e5, &fileTestDB{}) +} + +func TestReopen(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + f, err := ioutil.TempFile("", "ql-test-") + if err != nil { + t.Fatal(err) + } + + nm := f.Name() + if err = f.Close(); err != nil { + t.Fatal(err) + } + + defer func() { + if nm != "" { + os.Remove(nm) + } + }() + + db, err := OpenFile(nm, &Options{}) + if err != nil { + t.Error(err) + return + } + + for _, tn := range "abc" { + ql := fmt.Sprintf(` +BEGIN TRANSACTION; + CREATE TABLE %c (i int, s string); + INSERT INTO %c VALUES (%d, "<%c>"); +COMMIT; + `, tn, tn, tn-'a'+42, tn) + _, i, err := db.Run(NewRWCtx(), ql) + if err != nil { + db.Close() + t.Error(i, err) + return + } + } + + if err = db.Close(); err != nil { + t.Error(err) + return + } + + db, err = OpenFile(nm, &Options{}) + if err != nil { + t.Error(err) + return + } + + defer func() { + if err = db.Close(); err != nil { + t.Error(err) + } + }() + + if _, _, err = db.Run(NewRWCtx(), "BEGIN TRANSACTION; DROP TABLE b; COMMIT;"); err != nil { + t.Error(err) + return + } + + for _, tn := range "ac" { + ql := fmt.Sprintf("SELECT * FROM %c;", tn) + rs, i, err := db.Run(NewRWCtx(), ql) + if err != nil { + t.Error(i, err) + return + } + + if rs == nil { + t.Error(rs) + return + } + + rid := 0 + if err = rs[0].Do(false, func(r []interface{}) (bool, error) { + rid++ + if rid != 1 { + return false, fmt.Errorf("rid %d", rid) + } + + if g, e := recStr(r), fmt.Sprintf(`%d, "<%c>"`, tn-'a'+42, tn); g != e { + return false, fmt.Errorf("g `%s`, e `%s`", g, e) + } + + return true, nil + }); err != nil { + t.Error(err) + return + } + } +} + +func recStr(data []interface{}) string { + a := make([]string, len(data)) + for i, v := range data { + switch x := v.(type) { + case string: + a[i] = fmt.Sprintf("%q", x) + default: + a[i] = fmt.Sprint(x) + } + } + return strings.Join(a, ", ") +} + +//TODO +test long blob types with multiple inner chunks. + +func TestLastInsertID(t *testing.T) { + table := []struct { + ql string + id int + }{ + // 0 + {`BEGIN TRANSACTION; CREATE TABLE t (c int); COMMIT`, 0}, + {`BEGIN TRANSACTION; INSERT INTO t VALUES (41); COMMIT`, 1}, + {`BEGIN TRANSACTION; INSERT INTO t VALUES (42);`, 2}, + {`INSERT INTO t VALUES (43)`, 3}, + {`ROLLBACK; BEGIN TRANSACTION; INSERT INTO t VALUES (44); COMMIT`, 4}, + + //5 + {`BEGIN TRANSACTION; INSERT INTO t VALUES (45); COMMIT`, 5}, + {` + BEGIN TRANSACTION; + INSERT INTO t VALUES (46); // 6 + BEGIN TRANSACTION; + INSERT INTO t VALUES (47); // 7 + INSERT INTO t VALUES (48); // 8 + ROLLBACK; + INSERT INTO t VALUES (49); // 9 + COMMIT`, 9}, + {` + BEGIN TRANSACTION; + INSERT INTO t VALUES (50); // 10 + BEGIN TRANSACTION; + INSERT INTO t VALUES (51); // 11 + INSERT INTO t VALUES (52); // 12 + ROLLBACK; + COMMIT;`, 10}, + {`BEGIN TRANSACTION; INSERT INTO t VALUES (53); ROLLBACK`, 10}, + {`BEGIN TRANSACTION; INSERT INTO t VALUES (54); COMMIT`, 14}, + // 10 + {`BEGIN TRANSACTION; CREATE TABLE u (c int); COMMIT`, 14}, + {` + BEGIN TRANSACTION; + INSERT INTO t SELECT * FROM u; + COMMIT;`, 14}, + {`BEGIN TRANSACTION; INSERT INTO u VALUES (150); COMMIT`, 15}, + {` + BEGIN TRANSACTION; + INSERT INTO t SELECT * FROM u; + COMMIT;`, 16}, + } + + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + ctx := NewRWCtx() + for i, test := range table { + l, err := Compile(test.ql) + if err != nil { + t.Fatal(i, err) + } + + if _, _, err = db.Execute(ctx, l); err != nil { + t.Fatal(i, err) + } + + if g, e := ctx.LastInsertID, int64(test.id); g != e { + t.Fatal(i, g, e) + } + } +} + +func ExampleTCtx_lastInsertID() { + ins := MustCompile("BEGIN TRANSACTION; INSERT INTO t VALUES ($1); COMMIT;") + + db, err := OpenMem() + if err != nil { + panic(err) + } + + ctx := NewRWCtx() + if _, _, err = db.Run(ctx, ` + BEGIN TRANSACTION; + CREATE TABLE t (c int); + INSERT INTO t VALUES (1), (2), (3); + COMMIT; + `); err != nil { + panic(err) + } + + if _, _, err = db.Execute(ctx, ins, int64(42)); err != nil { + panic(err) + } + + id := ctx.LastInsertID + rs, _, err := db.Run(ctx, `SELECT * FROM t WHERE id() == $1`, id) + if err != nil { + panic(err) + } + + if err = rs[0].Do(false, func(data []interface{}) (more bool, err error) { + fmt.Println(data) + return true, nil + }); err != nil { + panic(err) + } + // Output: + // [42] +} + +func Example_recordsetFields() { + // See RecordSet.Fields documentation + + db, err := OpenMem() + if err != nil { + panic(err) + } + + rs, _, err := db.Run(NewRWCtx(), ` + BEGIN TRANSACTION; + CREATE TABLE t (s string, i int); + CREATE TABLE u (s string, i int); + INSERT INTO t VALUES + ("a", 1), + ("a", 2), + ("b", 3), + ("b", 4), + ; + INSERT INTO u VALUES + ("A", 10), + ("A", 20), + ("B", 30), + ("B", 40), + ; + COMMIT; + + SELECT t.s+u.s as a, t.i+u.i as b, "noName", "name" as Named FROM t, u; + + SELECT DISTINCT s as S, sum(i) as I FROM ( + SELECT t.s+u.s as s, t.i+u.i, 3 as i FROM t, u; + ) + GROUP BY s + ORDER BY I; + + SELECT DISTINCT * FROM ( + SELECT t.s+u.s as S, t.i+u.i, 3 as I FROM t, u; + ) + WHERE I < $1 + ORDER BY S; + `, 42) + if err != nil { + panic(err) + } + + for i, v := range rs { + fields, err := v.Fields() + switch { + case err != nil: + fmt.Printf("Fields[%d]: error: %s\n", i, err) + default: + fmt.Printf("Fields[%d]: %#v\n", i, fields) + } + } + // Output: + // Fields[0]: []string{"a", "b", "", "Named"} + // Fields[1]: []string{"S", "I"} + // Fields[2]: []string{"S", "", "I"} +} + +func TestRowsAffected(t *testing.T) { + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + ctx := NewRWCtx() + ctx.LastInsertID, ctx.RowsAffected = -1, -1 + if _, _, err = db.Run(ctx, ` + BEGIN TRANSACTION; + CREATE TABLE t (i int); + COMMIT; + `); err != nil { + t.Fatal(err) + } + + if g, e := ctx.LastInsertID, int64(0); g != e { + t.Fatal(g, e) + } + + if g, e := ctx.RowsAffected, int64(0); g != e { + t.Fatal(g, e) + } + + if _, _, err = db.Run(ctx, ` + BEGIN TRANSACTION; + INSERT INTO t VALUES (1), (2), (3); + COMMIT; + `); err != nil { + t.Fatal(err) + } + + if g, e := ctx.LastInsertID, int64(3); g != e { + t.Fatal(g, e) + } + + if g, e := ctx.RowsAffected, int64(3); g != e { + t.Fatal(g, e) + } + + if _, _, err = db.Run(ctx, ` + BEGIN TRANSACTION; + INSERT INTO t + SELECT * FROM t WHERE i == 2; + COMMIT; + `); err != nil { + t.Fatal(err) + } + + if g, e := ctx.LastInsertID, int64(4); g != e { + t.Fatal(g, e) + } + + if g, e := ctx.RowsAffected, int64(1); g != e { + t.Fatal(g, e) + } + + if _, _, err = db.Run(ctx, ` + BEGIN TRANSACTION; + DELETE FROM t WHERE i == 2; + COMMIT; + `); err != nil { + t.Fatal(err) + } + + if g, e := ctx.LastInsertID, int64(4); g != e { + t.Fatal(g, e) + } + + if g, e := ctx.RowsAffected, int64(2); g != e { + t.Fatal(g, e) + } + + if _, _, err = db.Run(ctx, ` + BEGIN TRANSACTION; + UPDATE t i = 42 WHERE i == 3; + COMMIT; + `); err != nil { + t.Fatal(err) + } + + if g, e := ctx.LastInsertID, int64(4); g != e { + t.Fatal(g, e) + } + + if g, e := ctx.RowsAffected, int64(1); g != e { + t.Fatal(g, e) + } +} + +func dumpDB(db *DB, tag string) (string, error) { + var buf bytes.Buffer + f := strutil.IndentFormatter(&buf, "\t") + f.Format("---- %s%i\n", tag) + for nm, tab := range db.root.tables { + hh := tab.hhead + h := tab.head + f.Format("%u%q: hhead %d, head %d, scols0 %q, scols %q%i\n", nm, hh, h, cols2meta(tab.cols0), cols2meta(tab.cols)) + for h != 0 { + rec, err := db.store.Read(nil, h, tab.cols...) + if err != nil { + return "", err + } + + f.Format("record @%d: %v\n", h, rec) + h = rec[0].(int64) + } + f.Format("%u") + for i, v := range tab.indices { + if v == nil { + continue + } + + xname := v.name + cname := "id()" + if i != 0 { + cname = tab.cols0[i-1].name + } + f.Format("index %s on %s%i\n", xname, cname) + it, _, err := v.x.Seek([]interface{}{nil}) + if err != nil { + return "", err + } + + for { + k, v, err := it.Next() + if err != nil { + if err == io.EOF { + break + } + + return "", err + } + + f.Format("%v: %v\n", k, v) + } + f.Format("%u") + } + } + + return buf.String(), nil +} + +func testIndices(db *DB, t *testing.T) { + ctx := NewRWCtx() + var err error + e := func(q string) { + s0, err := dumpDB(db, "pre\n\t"+q) + if err != nil { + t.Log(s0) + t.Fatal(err) + } + + if _, _, err = db.Run(ctx, q); err != nil { + t.Log(q) + t.Fatal(err) + } + + s, err := dumpDB(db, "post\n\t"+q) + if err != nil { + t.Log(s0) + t.Log(s) + t.Fatal(err) + } + + if db.isMem { + return + } + + nm := db.Name() + + if err = db.Close(); err != nil { + t.Log(s0) + t.Log(s) + t.Fatal(err) + } + + if db, err = OpenFile(nm, &Options{}); err != nil { + t.Log(s0) + t.Log(s) + t.Fatal(err) + } + + if s, err = dumpDB(db, "reopened"); err != nil { + t.Log(s0) + t.Log(s) + t.Fatal(err) + } + + } + + e(` BEGIN TRANSACTION; + CREATE TABLE t (i int); + COMMIT;`) + e(` BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS Index2 (TableName string); + CREATE INDEX x ON t (id()); + COMMIT;`) + e(` BEGIN TRANSACTION; + INSERT INTO t VALUES(42); + COMMIT;`) + e(` BEGIN TRANSACTION; + INSERT INTO t VALUES(24); + COMMIT;`) + e(` BEGIN TRANSACTION; + CREATE INDEX xi ON t (i); + COMMIT;`) + e(` BEGIN TRANSACTION; + INSERT INTO t VALUES(1); + COMMIT;`) + e(` BEGIN TRANSACTION; + INSERT INTO t VALUES(999); + COMMIT;`) + e(` BEGIN TRANSACTION; + UPDATE t i = 240 WHERE i == 24; + COMMIT;`) + e(` BEGIN TRANSACTION; + DELETE FROM t WHERE i == 240; + COMMIT;`) + e(` BEGIN TRANSACTION; + TRUNCATE TABLE t; + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i int, s string); + CREATE INDEX xi ON t (i); + INSERT INTO t VALUES (42, "foo"); + COMMIT;`) + e(` BEGIN TRANSACTION; + ALTER TABLE t DROP COLUMN i; + COMMIT;`) + + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + INSERT INTO t SELECT 10*i FROM t; + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + COMMIT; + BEGIN TRANSACTION; + INSERT INTO t SELECT 10*i FROM t; + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + DROP INDEX x; + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + COMMIT; + BEGIN TRANSACTION; + DROP INDEX x; + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + COMMIT;`) + e(` + BEGIN TRANSACTION; + DROP INDEX x; + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + ALTER TABLE t ADD s string; + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + COMMIT;`) + e(` BEGIN TRANSACTION; + ALTER TABLE t ADD s string; + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(42); + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP TABLE IF EXISTS t; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i+1, 2*i); // Non simple index. + COMMIT;`) + e(` BEGIN TRANSACTION; + DROP INDEX x; + COMMIT;`) + + if err = db.Close(); err != nil { + t.Fatal(err) + } +} + +func TestIndices(t *testing.T) { + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + testIndices(db, t) + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + dir, err := ioutil.TempDir("", "ql-test") + + if err != nil { + t.Fatal(err) + } + + defer func() { + os.RemoveAll(dir) + + }() + + nm := filepath.Join(dir, "ql.db") + db, err = OpenFile(nm, &Options{CanCreate: true}) + if err != nil { + t.Fatal(err) + } + + testIndices(db, t) +} + +var benchmarkInsertBoolOnce = map[string]sync.Once{} + +func benchmarkInsertBool(b *testing.B, db *DB, size int, selectivity float64, index bool, teardown func()) { + if testing.Verbose() { + benchProlog(b) + id := fmt.Sprintf("%t|%d|%g|%t", db.isMem, size, selectivity, index) + once := benchmarkInsertBoolOnce[id] + once.Do(func() { + s := "INDEXED" + if !index { + s = "NON " + s + } + b.Logf(`Insert %d records into a table having a single bool %s column. Batch size: 1 record. + +`, size, s) + }) + benchmarkInsertBoolOnce[id] = once + } + + if teardown != nil { + defer teardown() + } + + ctx := NewRWCtx() + if _, _, err := db.Run(ctx, ` + BEGIN TRANSACTION; + CREATE TABLE t (b bool); + `); err != nil { + b.Fatal(err) + } + + if index { + if _, _, err := db.Run(ctx, ` + CREATE INDEX x ON t (b); + `); err != nil { + b.Fatal(err) + } + } + + ins, err := Compile("INSERT INTO t VALUES($1);") + if err != nil { + b.Fatal(err) + } + + trunc, err := Compile("TRUNCATE TABLE t;") + if err != nil { + b.Fatal(err) + } + + begin, err := Compile("BEGIN TRANSACTION;") + if err != nil { + b.Fatal(err) + } + + commit, err := Compile("COMMIT;") + if err != nil { + b.Fatal(err) + } + + rng := rand.New(rand.NewSource(42)) + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + if i != 0 { + if _, _, err = db.Execute(ctx, begin); err != nil { + b.Fatal(err) + } + } + + if _, _, err = db.Execute(ctx, trunc); err != nil { + b.Fatal(err) + } + + b.StartTimer() + for j := 0; j < size; j++ { + if _, _, err = db.Execute(ctx, ins, rng.Float64() < selectivity); err != nil { + b.Fatal(err) + } + } + if _, _, err = db.Execute(ctx, commit); err != nil { + b.Fatal(err) + } + } + b.StopTimer() + b.SetBytes(int64(size) * benchScale) +} + +func benchmarkInsertBoolMem(b *testing.B, size int, sel float64, index bool) { + db, err := OpenMem() + if err != nil { + b.Fatal(err) + } + + benchmarkInsertBool(b, db, size, sel, index, nil) +} + +func BenchmarkInsertBoolMemNoX1e1(b *testing.B) { + benchmarkInsertBoolMem(b, 1e1, 0.5, false) +} + +func BenchmarkInsertBoolMemX1e1(b *testing.B) { + benchmarkInsertBoolMem(b, 1e1, 0.5, true) +} + +func BenchmarkInsertBoolMemNoX1e2(b *testing.B) { + benchmarkInsertBoolMem(b, 1e2, 0.5, false) +} + +func BenchmarkInsertBoolMemX1e2(b *testing.B) { + benchmarkInsertBoolMem(b, 1e2, 0.5, true) +} + +func BenchmarkInsertBoolMemNoX1e3(b *testing.B) { + benchmarkInsertBoolMem(b, 1e3, 0.5, false) +} + +func BenchmarkInsertBoolMemX1e3(b *testing.B) { + benchmarkInsertBoolMem(b, 1e3, 0.5, true) +} + +func BenchmarkInsertBoolMemNoX1e4(b *testing.B) { + benchmarkInsertBoolMem(b, 1e4, 0.5, false) +} + +func BenchmarkInsertBoolMemX1e4(b *testing.B) { + benchmarkInsertBoolMem(b, 1e4, 0.5, true) +} + +func BenchmarkInsertBoolMemNoX1e5(b *testing.B) { + benchmarkInsertBoolMem(b, 1e5, 0.5, false) +} + +func BenchmarkInsertBoolMemX1e5(b *testing.B) { + benchmarkInsertBoolMem(b, 1e5, 0.5, true) +} + +func benchmarkInsertBoolFile(b *testing.B, size int, sel float64, index bool) { + dir, err := ioutil.TempDir("", "ql-bench-") + if err != nil { + b.Fatal(err) + } + + n := runtime.GOMAXPROCS(0) + db, err := OpenFile(filepath.Join(dir, "ql.db"), &Options{CanCreate: true}) + if err != nil { + b.Fatal(err) + } + + benchmarkInsertBool(b, db, size, sel, index, func() { + runtime.GOMAXPROCS(n) + db.Close() + os.RemoveAll(dir) + }) +} + +func BenchmarkInsertBoolFileNoX1e1(b *testing.B) { + benchmarkInsertBoolFile(b, 1e1, 0.5, false) +} + +func BenchmarkInsertBoolFileX1e1(b *testing.B) { + benchmarkInsertBoolFile(b, 1e1, 0.5, true) +} + +func BenchmarkInsertBoolFileNoX1e2(b *testing.B) { + benchmarkInsertBoolFile(b, 1e2, 0.5, false) +} + +func BenchmarkInsertBoolFileX1e2(b *testing.B) { + benchmarkInsertBoolFile(b, 1e2, 0.5, true) +} + +func BenchmarkInsertBoolFileNoX1e3(b *testing.B) { + benchmarkInsertBoolFile(b, 1e3, 0.5, false) +} + +func BenchmarkInsertBoolFileX1e3(b *testing.B) { + benchmarkInsertBoolFile(b, 1e3, 0.5, true) +} + +var benchmarkSelectBoolOnce = map[string]sync.Once{} + +func benchmarkSelectBool(b *testing.B, db *DB, size int, selectivity float64, index bool, teardown func()) { + sel, err := Compile("SELECT * FROM t WHERE b;") + if err != nil { + b.Fatal(err) + } + + if testing.Verbose() { + benchProlog(b) + id := fmt.Sprintf("%t|%d|%g|%t", db.isMem, size, selectivity, index) + once := benchmarkSelectBoolOnce[id] + once.Do(func() { + s := "INDEXED" + if !index { + s = "NON " + s + } + b.Logf(`A table has a single %s bool column b. Insert %d records with a random bool value, +%.0f%% of them are true. Measure the performance of +%s +`, s, size, 100*selectivity, sel) + }) + benchmarkSelectBoolOnce[id] = once + } + + if teardown != nil { + defer teardown() + } + + ctx := NewRWCtx() + if _, _, err := db.Run(ctx, ` + BEGIN TRANSACTION; + CREATE TABLE t (b bool); + `); err != nil { + b.Fatal(err) + } + + if index { + if _, _, err := db.Run(ctx, ` + CREATE INDEX x ON t (b); + `); err != nil { + b.Fatal(err) + } + } + + ins, err := Compile("INSERT INTO t VALUES($1);") + if err != nil { + b.Fatal(err) + } + + var n int64 + rng := rand.New(rand.NewSource(42)) + for j := 0; j < size; j++ { + v := rng.Float64() < selectivity + if v { + n++ + } + if _, _, err = db.Execute(ctx, ins, v); err != nil { + b.Fatal(err) + } + } + + if _, _, err := db.Run(ctx, "COMMIT;"); err != nil { + b.Fatal(err) + } + + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + var m int64 + rss, _, err := db.Execute(nil, sel) + if err != nil { + b.Fatal(err) + } + + if err = rss[0].Do(false, func([]interface{}) (bool, error) { + m++ + return true, nil + }); err != nil { + b.Fatal(err) + } + if g, e := n, m; g != e { + b.Fatal(g, e) + } + } + b.StopTimer() + b.SetBytes(n * benchScale) +} + +func benchmarkSelectBoolMem(b *testing.B, size int, sel float64, index bool) { + db, err := OpenMem() + if err != nil { + b.Fatal(err) + } + + benchmarkSelectBool(b, db, size, sel, index, nil) +} + +// ---- + +func BenchmarkSelectBoolMemNoX1e1Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e1, 0.5, false) +} + +func BenchmarkSelectBoolMemX1e1Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e1, 0.5, true) +} + +func BenchmarkSelectBoolMemNoX1e2Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e2, 0.5, false) +} + +func BenchmarkSelectBoolMemX1e2Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e2, 0.5, true) +} + +func BenchmarkSelectBoolMemNoX1e3Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e3, 0.5, false) +} + +func BenchmarkSelectBoolMemX1e3Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e3, 0.5, true) +} + +func BenchmarkSelectBoolMemNoX1e4Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e4, 0.5, false) +} + +func BenchmarkSelectBoolMemX1e4Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e4, 0.5, true) +} + +func BenchmarkSelectBoolMemNoX1e5Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e5, 0.5, false) +} + +func BenchmarkSelectBoolMemX1e5Perc50(b *testing.B) { + benchmarkSelectBoolMem(b, 1e5, 0.5, true) +} + +// ---- + +func BenchmarkSelectBoolMemNoX1e1Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e1, 0.05, false) +} + +func BenchmarkSelectBoolMemX1e1Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e1, 0.05, true) +} + +func BenchmarkSelectBoolMemNoX1e2Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e2, 0.05, false) +} + +func BenchmarkSelectBoolMemX1e2Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e2, 0.05, true) +} + +func BenchmarkSelectBoolMemNoX1e3Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e3, 0.05, false) +} + +func BenchmarkSelectBoolMemX1e3Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e3, 0.05, true) +} + +func BenchmarkSelectBoolMemNoX1e4Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e4, 0.05, false) +} + +func BenchmarkSelectBoolMemX1e4Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e4, 0.05, true) +} + +func BenchmarkSelectBoolMemNoX1e5Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e5, 0.05, false) +} + +func BenchmarkSelectBoolMemX1e5Perc5(b *testing.B) { + benchmarkSelectBoolMem(b, 1e5, 0.05, true) +} + +func benchmarkSelectBoolFile(b *testing.B, size int, sel float64, index bool) { + dir, err := ioutil.TempDir("", "ql-bench-") + if err != nil { + b.Fatal(err) + } + + n := runtime.GOMAXPROCS(0) + db, err := OpenFile(filepath.Join(dir, "ql.db"), &Options{CanCreate: true}) + if err != nil { + b.Fatal(err) + } + + benchmarkSelectBool(b, db, size, sel, index, func() { + runtime.GOMAXPROCS(n) + db.Close() + os.RemoveAll(dir) + }) +} + +// ---- + +func BenchmarkSelectBoolFileNoX1e1Perc50(b *testing.B) { + benchmarkSelectBoolFile(b, 1e1, 0.5, false) +} + +func BenchmarkSelectBoolFileX1e1Perc50(b *testing.B) { + benchmarkSelectBoolFile(b, 1e1, 0.5, true) +} + +func BenchmarkSelectBoolFileNoX1e2Perc50(b *testing.B) { + benchmarkSelectBoolFile(b, 1e2, 0.5, false) +} + +func BenchmarkSelectBoolFileX1e2Perc50(b *testing.B) { + benchmarkSelectBoolFile(b, 1e2, 0.5, true) +} + +func BenchmarkSelectBoolFileNoX1e3Perc50(b *testing.B) { + benchmarkSelectBoolFile(b, 1e3, 0.5, false) +} + +func BenchmarkSelectBoolFileX1e3Perc50(b *testing.B) { + benchmarkSelectBoolFile(b, 1e3, 0.5, true) +} + +func BenchmarkSelectBoolFileNoX1e4Perc50(b *testing.B) { + benchmarkSelectBoolFile(b, 1e4, 0.5, false) +} + +func BenchmarkSelectBoolFileX1e4Perc50(b *testing.B) { + benchmarkSelectBoolFile(b, 1e4, 0.5, true) +} + +// ---- + +func BenchmarkSelectBoolFileNoX1e1Perc5(b *testing.B) { + benchmarkSelectBoolFile(b, 1e1, 0.05, false) +} + +func BenchmarkSelectBoolFileX1e1Perc5(b *testing.B) { + benchmarkSelectBoolFile(b, 1e1, 0.05, true) +} + +func BenchmarkSelectBoolFileNoX1e2Perc5(b *testing.B) { + benchmarkSelectBoolFile(b, 1e2, 0.05, false) +} + +func BenchmarkSelectBoolFileX1e2Perc5(b *testing.B) { + benchmarkSelectBoolFile(b, 1e2, 0.05, true) +} + +func BenchmarkSelectBoolFileNoX1e3Perc5(b *testing.B) { + benchmarkSelectBoolFile(b, 1e3, 0.05, false) +} + +func BenchmarkSelectBoolFileX1e3Perc5(b *testing.B) { + benchmarkSelectBoolFile(b, 1e3, 0.05, true) +} + +func BenchmarkSelectBoolFileNoX1e4Perc5(b *testing.B) { + benchmarkSelectBoolFile(b, 1e4, 0.05, false) +} + +func BenchmarkSelectBoolFileX1e4Perc5(b *testing.B) { + benchmarkSelectBoolFile(b, 1e4, 0.05, true) +} + +func TestIndex(t *testing.T) { + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + ctx := NewRWCtx() + if _, _, err := db.Run(ctx, ` + BEGIN TRANSACTION; + CREATE TABLE t (b bool); + `); err != nil { + t.Fatal(err) + } + + if _, _, err := db.Run(ctx, ` + CREATE INDEX x ON t (b); + `); err != nil { + t.Fatal(err) + } + + ins, err := Compile("INSERT INTO t VALUES($1);") + if err != nil { + t.Fatal(err) + } + + size, selectivity := int(1e1), 0.5 + rng := rand.New(rand.NewSource(42)) + var n int64 + for j := 0; j < size; j++ { + v := rng.Float64() < selectivity + if v { + n++ + t.Logf("id %d <- true", j+1) + } + if _, _, err = db.Execute(ctx, ins, v); err != nil { + t.Fatal(err) + } + } + + if _, _, err := db.Run(ctx, "COMMIT;"); err != nil { + t.Fatal(err) + } + + s, err := dumpDB(db, "") + if err != nil { + t.Fatal(err) + } + + t.Logf("n: %d\n%s", n, s) + sel, err := Compile("SELECT id(), b FROM t WHERE b;") + if err != nil { + t.Fatal(err) + } + + var m int64 + rss, _, err := db.Execute(nil, sel) + if err != nil { + t.Fatal(err) + } + + if err = rss[0].Do(false, func(rec []interface{}) (bool, error) { + t.Logf("%v", rec) + m++ + return true, nil + }); err != nil { + t.Fatal(err) + } + + if g, e := n, m; g != e { + t.Fatal(g, e) + } +} + +var benchmarkCrossJoinOnce = map[string]sync.Once{} + +func benchmarkCrossJoin(b *testing.B, db *DB, create, sel List, size1, size2 int, index bool, teardown func()) { + if testing.Verbose() { + benchProlog(b) + id := fmt.Sprintf("%t|%d|%d|%t", db.isMem, size1, size2, index) + once := benchmarkCrossJoinOnce[id] + once.Do(func() { + s := "INDEXED " + if !index { + s = "NON " + s + } + b.Logf(`Fill two %stables with %d and %d records of random numbers [0, 1). Measure the performance of +%s +`, s, size1, size2, sel) + }) + benchmarkCrossJoinOnce[id] = once + } + + if teardown != nil { + defer teardown() + } + + ctx := NewRWCtx() + if _, _, err := db.Execute(ctx, create); err != nil { + b.Fatal(err) + } + + if index { + if _, _, err := db.Execute(ctx, xjoinX); err != nil { + b.Fatal(err) + } + } + + rng := rand.New(rand.NewSource(42)) + for i := 0; i < size1; i++ { + if _, _, err := db.Execute(ctx, xjoinT, rng.Float64()); err != nil { + b.Fatal(err) + } + } + for i := 0; i < size2; i++ { + if _, _, err := db.Execute(ctx, xjoinU, rng.Float64()); err != nil { + b.Fatal(err) + } + } + + if _, _, err := db.Run(ctx, "COMMIT"); err != nil { + b.Fatal(err) + } + + rs, _, err := db.Execute(nil, sel) + if err != nil { + b.Fatal(err) + } + + var n int + debug.FreeOSMemory() + b.ResetTimer() + for i := 0; i < b.N; i++ { + n = 0 + if err := rs[0].Do(false, func(rec []interface{}) (bool, error) { + n++ + return true, nil + }); err != nil { + b.Fatal(err) + } + } + b.StopTimer() + b.SetBytes(int64(n) * benchScale) +} + +var ( + xjoinCreate = MustCompile(`BEGIN TRANSACTION; + CREATE TABLE t (f float); + CREATE TABLE u (f float);`) + xjoinSel = MustCompile(`SELECT * FROM (SELECT f FROM t WHERE f < 0.1), (SELECT f FROM u where f < 0.1);`) + xjoinT = MustCompile("INSERT INTO t VALUES($1);") + xjoinU = MustCompile("INSERT INTO u VALUES($1);") + xjoinX = MustCompile(`CREATE INDEX x ON t (f); CREATE INDEX y ON u (f);`) +) + +func benchmarkCrossJoinMem(b *testing.B, size1, size2 int, index bool) { + db, err := OpenMem() + if err != nil { + b.Fatal(err) + } + + benchmarkCrossJoin(b, db, xjoinCreate, xjoinSel, size1, size2, index, nil) +} + +func benchmarkCrossJoinFile(b *testing.B, size1, size2 int, index bool) { + dir, err := ioutil.TempDir("", "ql-bench-") + if err != nil { + b.Fatal(err) + } + + n := runtime.GOMAXPROCS(0) + db, err := OpenFile(filepath.Join(dir, "ql.db"), &Options{CanCreate: true}) + if err != nil { + b.Fatal(err) + } + + benchmarkCrossJoin(b, db, xjoinCreate, xjoinSel, size1, size2, index, func() { + runtime.GOMAXPROCS(n) + db.Close() + os.RemoveAll(dir) + }) +} + +func BenchmarkCrossJoinMem1e1NoX1e2(b *testing.B) { + benchmarkCrossJoinMem(b, 1e1, 1e2, false) +} + +func BenchmarkCrossJoinMem1e1X1e2(b *testing.B) { + benchmarkCrossJoinMem(b, 1e1, 1e2, true) +} + +func BenchmarkCrossJoinMem1e2NoX1e3(b *testing.B) { + benchmarkCrossJoinMem(b, 1e2, 1e3, false) +} + +func BenchmarkCrossJoinMem1e2X1e3(b *testing.B) { + benchmarkCrossJoinMem(b, 1e2, 1e3, true) +} + +func BenchmarkCrossJoinMem1e3NoX1e4(b *testing.B) { + benchmarkCrossJoinMem(b, 1e3, 1e4, false) +} + +func BenchmarkCrossJoinMem1e3X1e4(b *testing.B) { + benchmarkCrossJoinMem(b, 1e3, 1e4, true) +} + +func BenchmarkCrossJoinMem1e2NoX1e1(b *testing.B) { + benchmarkCrossJoinMem(b, 1e2, 1e1, false) +} + +func BenchmarkCrossJoinMem1e2X1e1(b *testing.B) { + benchmarkCrossJoinMem(b, 1e2, 1e1, true) +} + +func BenchmarkCrossJoinMem1e3NoX1e2(b *testing.B) { + benchmarkCrossJoinMem(b, 1e3, 1e2, false) +} + +func BenchmarkCrossJoinMem1e3X1e2(b *testing.B) { + benchmarkCrossJoinMem(b, 1e3, 1e2, true) +} + +func BenchmarkCrossJoinMem1e4NoX1e3(b *testing.B) { + benchmarkCrossJoinMem(b, 1e4, 1e3, false) +} + +func BenchmarkCrossJoinMem1e4X1e3(b *testing.B) { + benchmarkCrossJoinMem(b, 1e4, 1e3, true) +} + +// ---- + +func BenchmarkCrossJoinFile1e1NoX1e2(b *testing.B) { + benchmarkCrossJoinFile(b, 1e1, 1e2, false) +} + +func BenchmarkCrossJoinFile1e1X1e2(b *testing.B) { + benchmarkCrossJoinFile(b, 1e1, 1e2, true) +} + +func BenchmarkCrossJoinFile1e2NoX1e3(b *testing.B) { + benchmarkCrossJoinFile(b, 1e2, 1e3, false) +} + +func BenchmarkCrossJoinFile1e2X1e3(b *testing.B) { + benchmarkCrossJoinFile(b, 1e2, 1e3, true) +} + +func BenchmarkCrossJoinFile1e3NoX1e4(b *testing.B) { + benchmarkCrossJoinFile(b, 1e3, 1e4, false) +} + +func BenchmarkCrossJoinFile1e3X1e4(b *testing.B) { + benchmarkCrossJoinFile(b, 1e3, 1e4, true) +} + +func BenchmarkCrossJoinFile1e2NoX1e1(b *testing.B) { + benchmarkCrossJoinFile(b, 1e2, 1e1, false) +} + +func BenchmarkCrossJoinFile1e2X1e1(b *testing.B) { + benchmarkCrossJoinFile(b, 1e2, 1e1, true) +} + +func BenchmarkCrossJoinFile1e3NoX1e2(b *testing.B) { + benchmarkCrossJoinFile(b, 1e3, 1e2, false) +} + +func BenchmarkCrossJoinFile1e3X1e2(b *testing.B) { + benchmarkCrossJoinFile(b, 1e3, 1e2, true) +} + +func BenchmarkCrossJoinFile1e4NoX1e3(b *testing.B) { + benchmarkCrossJoinFile(b, 1e4, 1e3, false) +} + +func BenchmarkCrossJoinFile1e4X1e3(b *testing.B) { + benchmarkCrossJoinFile(b, 1e4, 1e3, true) +} + +func TestIssue35(t *testing.T) { + var bigInt big.Int + var bigRat big.Rat + bigInt.SetInt64(42) + bigRat.SetInt64(24) + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + ctx := NewRWCtx() + _, _, err = db.Run(ctx, ` + BEGIN TRANSACTION; + CREATE TABLE t (i bigint, r bigrat); + INSERT INTO t VALUES ($1, $2); + COMMIT; + `, bigInt, bigRat) + if err != nil { + t.Fatal(err) + } + + bigInt.SetInt64(420) + bigRat.SetInt64(240) + + rs, _, err := db.Run(nil, "SELECT * FROM t;") + if err != nil { + t.Fatal(err) + } + + n := 0 + if err := rs[0].Do(false, func(rec []interface{}) (bool, error) { + switch n { + case 0: + n++ + if g, e := fmt.Sprint(rec), "[42 24/1]"; g != e { + t.Fatal(g, e) + } + + return true, nil + default: + t.Fatal(n) + panic("unreachable") + } + }); err != nil { + t.Fatal(err) + } +} + +func TestIssue28(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + RegisterDriver() + dir, err := ioutil.TempDir("", "ql-test-") + if err != nil { + t.Fatal(err) + } + + defer os.RemoveAll(dir) + pth := filepath.Join(dir, "ql.db") + sdb, err := sql.Open("ql", "file://"+pth) + if err != nil { + t.Fatal(err) + } + + defer sdb.Close() + tx, err := sdb.Begin() + if err != nil { + t.Fatal(err) + } + + if _, err = tx.Exec("CREATE TABLE t (i int);"); err != nil { + t.Fatal(err) + } + + if err = tx.Commit(); err != nil { + t.Fatal(err) + } + + if _, err = os.Stat(pth); err != nil { + t.Fatal(err) + } + + pth = filepath.Join(dir, "mem.db") + mdb, err := sql.Open("ql", "memory://"+pth) + if err != nil { + t.Fatal(err) + } + + defer mdb.Close() + if tx, err = mdb.Begin(); err != nil { + t.Fatal(err) + } + + if _, err = tx.Exec("CREATE TABLE t (i int);"); err != nil { + t.Fatal(err) + } + + if err = tx.Commit(); err != nil { + t.Fatal(err) + } + + if _, err = os.Stat(pth); err == nil { + t.Fatal(err) + } +} + +func dumpFields(f []*fld) string { + a := []string{} + for _, v := range f { + a = append(a, fmt.Sprintf("%p: %q", v, v.name)) + } + return strings.Join(a, ", ") +} + +func rndBytes(n int, seed int64) []byte { + rng := rand.New(rand.NewSource(seed)) + b := make([]byte, n) + for i := range b { + b[i] = byte(rng.Int()) + } + return b +} + +func TestIssue50(t *testing.T) { // https://github.com/cznic/ql/issues/50 + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + const dbFileName = "scans.qldb" + + type Scan struct { + ID int + Jobname string + Timestamp time.Time + Data []byte + + X, Y, Z float64 + Alpha, Beta, Gamma float64 + } + + // querys + const dbCreateTables = ` +CREATE TABLE IF NOT EXISTS Scans ( + X float, + Y float, + Z float, + Alpha float, + Beta float, + Gamma float, + Timestamp time, + Jobname string, + Data blob +); +CREATE INDEX IF NOT EXISTS ScansId on Scans (id()); +` + + const dbInsertScan = ` +INSERT INTO Scans (Timestamp,Jobname,X,Y,Z,Alpha,Beta,Gamma,Data) VALUES( +$1, +$2, +$3,$4,$5, +$6,$7,$8, +$9 +); +` + + const dbSelectOverview = `SELECT id() as ID, Jobname, Timestamp, Data, Y,Z, Gamma From Scans;` + + dir, err := ioutil.TempDir("", "ql-test-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + // create db + t.Log("Opening db.") + RegisterDriver() + db, err := sql.Open("ql", filepath.Join(dir, dbFileName)) + if err != nil { + t.Fatal(err) + } + defer db.Close() + + tx, err := db.Begin() + if err != nil { + return + } + _, err = tx.Exec(dbCreateTables) + if err != nil { + t.Fatal("could not create Table.", err) + } + + err = tx.Commit() + if err != nil { + t.Fatal("could not commit transaction.", err) + } + + // insert some data + tx, err = db.Begin() + if err != nil { + t.Fatalf("db.Begin() Error - %v", err) + } + + stmt, err := tx.Prepare(dbInsertScan) + if err != nil { + t.Fatalf("tx.Prepare(dbInsertScan) Error - %v", err) + } + defer stmt.Close() + + scanFnames := []string{"1.xyz", "2.xyz", "3.xyz"} + for _, fname := range scanFnames { + scanData, err := ioutil.ReadFile(filepath.Join("_testdata", fname)) + if err != nil { + t.Fatalf("ioutil.ReadFile(%s) Error - %v", fname, err) + } + + // hash before insert + h := md5.New() + h.Write(scanData) + + t.Logf("md5 of %s: %x", fname, h.Sum(nil)) + + _, err = stmt.Exec(time.Now(), "Job-0815", 1.0, 2.0, 3.0, 0.1, 0.2, 0.3, scanData) + if err != nil { + t.Fatalf("stmt.Exec() Error - %v", err) + return + } + } + + err = tx.Commit() + if err != nil { + t.Fatalf("tx.Commit() Error - %v", err) + } + + // select the inserted data + rows, err := db.Query(dbSelectOverview) + if err != nil { + t.Fatalf("db.Query(dbSelectOverview) Error - %v", err) + } + defer rows.Close() + + var scans []Scan + for rows.Next() { + var s Scan + var data []byte + + err = rows.Scan(&s.ID, &s.Jobname, &s.Timestamp, &data, &s.Y, &s.Z, &s.Gamma) + if err != nil { + t.Fatalf("rows.Scan(&s..) Error - %v", err) + } + scans = append(scans, s) + + // hash after select + h := md5.New() + h.Write(data) + + t.Logf("md5 of %d: %x", s.ID, h.Sum(nil)) + } + + err = rows.Err() + if err != nil { + t.Fatalf("rows.Err() Error - %v", err) + } + + t.Log("Done:", scans) +} + +func TestIssue56(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + var schema = ` +CREATE TABLE IF NOT EXISTS Test ( + A string, + B string, + Suppressed bool, +); +CREATE INDEX IF NOT EXISTS aIdx ON Test (A); +CREATE INDEX IF NOT EXISTS bIdx ON Test (B); +` + + RegisterDriver() + dir, err := ioutil.TempDir("", "ql-test-") + if err != nil { + t.Fatal(err) + } + + defer os.RemoveAll(dir) + pth := filepath.Join(dir, "test.db") + db, err := sql.Open("ql", "file://"+pth) + if err != nil { + t.Fatal(err) + } + + defer db.Close() + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + + _, err = tx.Exec(schema) + if err != nil { + t.Fatal(err) + } + + err = tx.Commit() + if err != nil { + t.Fatal(err) + } + + // Open a new transaction and do a SELECT + + tx, err = db.Begin() + if err != nil { + t.Fatal(err) + } + + var id int64 + row := tx.QueryRow("SELECT * FROM Test") + err = row.Scan(&id) // <-- Blocks here + + switch err { + case sql.ErrNoRows: + break + case nil: + break + default: + t.Fatal(err) + } + + tx.Rollback() + return +} + +func TestRecordSetRows(t *testing.T) { + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + rss, _, err := db.Run(NewRWCtx(), ` + BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (1), (2), (3), (4), (5); + COMMIT; + SELECT * FROM t ORDER BY i; + `) + if err != nil { + t.Fatal(err) + } + + tab := []struct { + limit, offset int + result []int64 + }{ + // 0 + {-1, 0, []int64{1, 2, 3, 4, 5}}, + {0, 0, nil}, + {1, 0, []int64{1}}, + {2, 0, []int64{1, 2}}, + {3, 0, []int64{1, 2, 3}}, + // 5 + {4, 0, []int64{1, 2, 3, 4}}, + {5, 0, []int64{1, 2, 3, 4, 5}}, + {6, 0, []int64{1, 2, 3, 4, 5}}, + {-1, 0, []int64{1, 2, 3, 4, 5}}, + {-1, 1, []int64{2, 3, 4, 5}}, + // 10 + {-1, 2, []int64{3, 4, 5}}, + {-1, 3, []int64{4, 5}}, + {-1, 4, []int64{5}}, + {-1, 5, nil}, + {3, 0, []int64{1, 2, 3}}, + // 15 + {3, 1, []int64{2, 3, 4}}, + {3, 2, []int64{3, 4, 5}}, + {3, 3, []int64{4, 5}}, + {3, 4, []int64{5}}, + {3, 5, nil}, + // 20 + {-1, 2, []int64{3, 4, 5}}, + {0, 2, nil}, + {1, 2, []int64{3}}, + {2, 2, []int64{3, 4}}, + {3, 2, []int64{3, 4, 5}}, + // 25 + {4, 2, []int64{3, 4, 5}}, + } + + rs := rss[0] + for iTest, test := range tab { + t.Log(iTest) + rows, err := rs.Rows(test.limit, test.offset) + if err != nil { + t.Fatal(err) + } + + if g, e := len(rows), len(test.result); g != e { + t.Log(rows, test.result) + t.Fatal(g, e) + } + + for i, row := range rows { + if g, e := len(row), 1; g != e { + t.Fatal(i, g, i) + } + + if g, e := row[0], test.result[i]; g != e { + t.Fatal(i, g, e) + } + } + } +} + +func TestRecordFirst(t *testing.T) { + q := MustCompile("SELECT * FROM t WHERE i > $1 ORDER BY i;") + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + if _, _, err = db.Run(NewRWCtx(), ` + BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (1), (2), (3), (4), (5); + COMMIT; + `); err != nil { + t.Fatal(err) + } + + tab := []struct { + par int64 + result int64 + }{ + {-1, 1}, + {0, 1}, + {1, 2}, + {2, 3}, + {3, 4}, + {4, 5}, + {5, -1}, + } + + for iTest, test := range tab { + t.Log(iTest) + rss, _, err := db.Execute(nil, q, test.par) + if err != nil { + t.Fatal(err) + } + + row, err := rss[0].FirstRow() + if err != nil { + t.Fatal(err) + } + + switch { + case test.result < 0: + if row != nil { + t.Fatal(row) + } + default: + if row == nil { + t.Fatal(row) + } + + if g, e := len(row), 1; g != e { + t.Fatal(g, e) + } + + if g, e := row[0], test.result; g != e { + t.Fatal(g, e) + } + } + } +} + +var issue63 = MustCompile(` +BEGIN TRANSACTION; + CREATE TABLE Forecast (WeatherProvider string, Timestamp time, MinTemp int32, MaxTemp int32); + INSERT INTO Forecast VALUES ("dwd.de", now(), 20, 22); +COMMIT; +SELECT * FROM Forecast WHERE Timestamp > 0;`) + +func TestIssue63(t *testing.T) { + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + rs, _, err := db.Execute(NewRWCtx(), issue63) + if err != nil { + t.Fatal(err) + } + + if _, err = rs[0].Rows(-1, 0); err == nil { + t.Fatal(err) + } + + t.Log(err) + if g, e := strings.Contains(err.Error(), "invalid operation"), true; g != e { + t.Fatal(g, e) + } + + if g, e := strings.Contains(err.Error(), "mismatched types time.Time and int64"), true; g != e { + t.Fatal(g, e) + } +} + +const issue66Src = ` +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE UNIQUE INDEX x ON t (i); + INSERT INTO t VALUES (1), (1); +COMMIT;` + +var issue66 = MustCompile(issue66Src) + +func TestIssue66Mem(t *testing.T) { + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + _, _, err = db.Execute(NewRWCtx(), issue66) + if err == nil { + t.Fatal(err) + } + + t.Log(err) +} + +func TestIssue66File(t *testing.T) { + dir, err := ioutil.TempDir("", "ql-test-") + if err != nil { + t.Fatal(err) + } + + defer os.RemoveAll(dir) + + db, err := OpenFile(filepath.Join(dir, "test.db"), &Options{CanCreate: true}) + if err != nil { + t.Fatal(err) + } + + defer db.Close() + + _, _, err = db.Execute(NewRWCtx(), issue66) + if err == nil { + t.Fatal(err) + } + + t.Log(err) +} + +func TestIssue66MemDriver(t *testing.T) { + RegisterMemDriver() + db, err := sql.Open("ql-mem", "TestIssue66MemDriver-"+fmt.Sprintf("%d", time.Now().UnixNano())) + if err != nil { + t.Fatal(err) + } + + defer db.Close() + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + + if _, err = tx.Exec(issue66Src); err == nil { + t.Fatal(err) + } + + t.Log(err) +} + +func TestIssue66FileDriver(t *testing.T) { + RegisterDriver() + dir, err := ioutil.TempDir("", "ql-test-") + if err != nil { + t.Fatal(err) + } + + defer os.RemoveAll(dir) + + db, err := sql.Open("ql", filepath.Join(dir, "TestIssue66MemDriver")) + if err != nil { + t.Fatal(err) + } + + defer db.Close() + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + + if _, err = tx.Exec(issue66Src); err == nil { + t.Fatal(err) + } + + t.Log(err) +} + +func Example_lIKE() { + db, err := OpenMem() + if err != nil { + panic(err) + } + + rss, _, err := db.Run(NewRWCtx(), ` + BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + INSERT INTO t VALUES + (1, "seafood"), + (2, "A fool on the hill"), + (3, NULL), + (4, "barbaz"), + (5, "foobar"), + ; + COMMIT; + + SELECT * FROM t WHERE s LIKE "foo" ORDER BY i; + SELECT * FROM t WHERE s LIKE "^bar" ORDER BY i; + SELECT * FROM t WHERE s LIKE "bar$" ORDER BY i; + SELECT * FROM t WHERE !(s LIKE "foo") ORDER BY i;`, + ) + if err != nil { + panic(err) + } + + for _, rs := range rss { + if err := rs.Do(false, func(data []interface{}) (bool, error) { + fmt.Println(data) + return true, nil + }); err != nil { + panic(err) + } + fmt.Println("----") + } + // Output: + // [1 seafood] + // [2 A fool on the hill] + // [5 foobar] + // ---- + // [4 barbaz] + // ---- + // [5 foobar] + // ---- + // [4 barbaz] + // ---- +} + +func TestIssue73(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + RegisterDriver() + dir, err := ioutil.TempDir("", "ql-test-") + if err != nil { + t.Fatal(err) + } + + defer os.RemoveAll(dir) + pth := filepath.Join(dir, "test.db") + + for i := 0; i < 10; i++ { + var db *sql.DB + var tx *sql.Tx + var err error + var row *sql.Row + var name string + + if db, err = sql.Open("ql", pth); err != nil { + t.Fatal("sql.Open: ", err) + } + + t.Log("Call to db.Begin()") + if tx, err = db.Begin(); err != nil { + t.Fatal("db.Begin: ", err) + } + + t.Log("Call to tx.QueryRow()") + row = tx.QueryRow(`SELECT Name FROM __Table`) + t.Log("Call to tx.Commit()") + if err = tx.Commit(); err != nil { + t.Fatal("tx.Commit: ", err) + } + + row.Scan(&name) + t.Log("name: ", name) + } +} + +func Example_id() { + db, err := OpenMem() + if err != nil { + panic(err) + } + + rss, _, err := db.Run(NewRWCtx(), ` + BEGIN TRANSACTION; + CREATE TABLE foo (i int); + INSERT INTO foo VALUES (10), (20); + CREATE TABLE bar (fooID int, s string); + INSERT INTO bar SELECT id(), "ten" FROM foo WHERE i == 10; + INSERT INTO bar SELECT id(), "twenty" FROM foo WHERE i == 20; + COMMIT; + SELECT * + FROM foo, bar + WHERE bar.fooID == id(foo) + ORDER BY id(foo);`, + ) + if err != nil { + panic(err) + } + + for _, rs := range rss { + if err := rs.Do(false, func(data []interface{}) (bool, error) { + fmt.Println(data) + return true, nil + }); err != nil { + panic(err) + } + fmt.Println("----") + } + // Output: + // [10 1 ten] + // [20 2 twenty] + // ---- +} + +func eqRows(a, b [][]interface{}) bool { + if len(a) != len(b) { + return false + } + + for i, rowa := range a { + rowb := b[i] + if len(rowa) != len(rowb) { + return false + } + + for j, va := range rowa { + if va != rowb[j] { + return false + } + } + } + return true +} + +func TestInPredicateBug(t *testing.T) { + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + + if _, _, err := db.Run(NewRWCtx(), ` + BEGIN TRANSACTION; + CREATE TABLE all (i int); + INSERT INTO all VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9); + CREATE TABLE even (i int); + INSERT INTO even VALUES (0), (2), (4), (6), (8); + COMMIT; + `); err != nil { + t.Fatal(err) + } + + q := MustCompile(`SELECT * FROM all WHERE i IN (SELECT * FROM even) ORDER BY i`) + rs, _, err := db.Execute(nil, q) + if err != nil { + t.Fatal(err) + } + + rows, err := rs[0].Rows(-1, 0) + if err != nil { + t.Fatal(err) + } + + if g, e := rows, [][]interface{}{{int64(0)}, {int64(2)}, {int64(4)}, {int64(6)}, {int64(8)}}; !eqRows(g, e) { + t.Fatalf("\n%v\n%v", g, e) + } + + if _, _, err := db.Run(NewRWCtx(), ` + BEGIN TRANSACTION; + TRUNCATE TABLE even; + INSERT INTO even VALUES (1), (3), (5); + COMMIT; + `); err != nil { + t.Fatal(err) + } + + if rs, _, err = db.Execute(nil, q); err != nil { + t.Fatal(err) + } + + if rows, err = rs[0].Rows(-1, 0); err != nil { + t.Fatal(err) + } + + if g, e := rows, [][]interface{}{{int64(1)}, {int64(3)}, {int64(5)}}; !eqRows(g, e) { + t.Fatalf("\n%v\n%v", g, e) + } +} + +func testMentionedColumns(s stmt) (err error) { + defer func() { + if e := recover(); e != nil { + switch x := e.(type) { + case error: + err = x + default: + err = fmt.Errorf("error: %v", e) + } + } + }() + + switch x := s.(type) { + case + *alterTableAddStmt, + *alterTableDropColumnStmt, + beginTransactionStmt, + *createTableStmt, + commitStmt, + *dropIndexStmt, + *dropTableStmt, + *explainStmt, + rollbackStmt, + *truncateTableStmt: + // nop + case *createIndexStmt: + for _, e := range x.exprList { + mentionedColumns(e) + } + case *deleteStmt: + if e := x.where; e != nil { + mentionedColumns(e) + } + case *insertIntoStmt: + for _, ll := range x.lists { + for _, e := range ll { + mentionedColumns(e) + } + } + case *selectStmt: + for _, f := range x.flds { + mentionedColumns(f.expr) + } + if l := x.limit; l != nil { + mentionedColumns(l.expr) + } + if o := x.offset; o != nil { + mentionedColumns(o.expr) + } + if o := x.order; o != nil { + for _, e := range o.by { + mentionedColumns(e) + } + } + if w := x.where; w != nil { + mentionedColumns(w.expr) + } + case *updateStmt: + for _, v := range x.list { + mentionedColumns(v.expr) + } + if e := x.where; e != nil { + mentionedColumns(e) + } + default: + panic("internal error 056") + } + return nil +} + +const ( + issue99RowsToInsert = 100 + issue99Cycles = 100 +) + +var ( + fieldsIssue99 = []string{ + "Datacenter", + "Name", + "Address", + "Health", + "C0", + "C1", + "C2", + "C3", + "C4", + "C5", + "C6", + "C7", + "C8", + "C9", + "C10", + "C11", + "C12", + "C13", + "C14", + "C15", + "C16", + "C17", + "C18", + "C19", + "C20", + "C21", + "C22", + "C23", + "C24", + "C25", + "C26", + "C27", + "C28", + "C29", + "C30", + "C31", + "C32", + "C33", + "C34", + "C35", + "C36", + "C37", + "C38", + "C39", + "C40", + "C41", + "C42", + "C43", + "C44", + "C45", + "C46", + "C47", + "C48", + "C49", + "C50", + "C51", + "C52", + "C53", + "C54", + "C55", + "C56", + "C57", + "C58", + "C59", + "C60", + "C61", + "C62", + "C63", + "C64", + "C65", + "C66", + "C67", + "C68", + "C69", + "C70", + "C71", + "C72", + "C73", + "C74", + "C75", + "C76", + "C77", + "C78", + "C79", + "C80", + "C81", + "C82", + "C83", + "C84", + "C85", + "C86", + "C87", + "C88", + "C89", + "C90", + "C91", + "C92", + "C93", + "C94", + "C95", + "C96", + "C97", + "C98", + "C99", + } + + valuesIssue99 = make([]interface{}, len(fieldsIssue99)) +) + +func init() { + for i := range valuesIssue99 { + s := "" + for _, v := range rand.Perm(32) { + s += string('0' + v) + } + valuesIssue99[i] = s + } + valuesIssue99[3] = true +} + +func createTablesIssue99(db *sql.DB) error { + tx, err := db.Begin() + if err != nil { + return err + } + + if _, err = tx.Exec(` + DROP TABLE IF EXISTS Node; + CREATE TABLE Node ( + Datacenter string, + Name string, + Address string, + Health bool, + C0 string DEFAULT "", + C1 string DEFAULT "", + C2 string DEFAULT "", + C3 string DEFAULT "", + C4 string DEFAULT "", + C5 string DEFAULT "", + C6 string DEFAULT "", + C7 string DEFAULT "", + C8 string DEFAULT "", + C9 string DEFAULT "", + C10 string DEFAULT "", + C11 string DEFAULT "", + C12 string DEFAULT "", + C13 string DEFAULT "", + C14 string DEFAULT "", + C15 string DEFAULT "", + C16 string DEFAULT "", + C17 string DEFAULT "", + C18 string DEFAULT "", + C19 string DEFAULT "", + C20 string DEFAULT "", + C21 string DEFAULT "", + C22 string DEFAULT "", + C23 string DEFAULT "", + C24 string DEFAULT "", + C25 string DEFAULT "", + C26 string DEFAULT "", + C27 string DEFAULT "", + C28 string DEFAULT "", + C29 string DEFAULT "", + C30 string DEFAULT "", + C31 string DEFAULT "", + C32 string DEFAULT "", + C33 string DEFAULT "", + C34 string DEFAULT "", + C35 string DEFAULT "", + C36 string DEFAULT "", + C37 string DEFAULT "", + C38 string DEFAULT "", + C39 string DEFAULT "", + C40 string DEFAULT "", + C41 string DEFAULT "", + C42 string DEFAULT "", + C43 string DEFAULT "", + C44 string DEFAULT "", + C45 string DEFAULT "", + C46 string DEFAULT "", + C47 string DEFAULT "", + C48 string DEFAULT "", + C49 string DEFAULT "", + C50 string DEFAULT "", + C51 string DEFAULT "", + C52 string DEFAULT "", + C53 string DEFAULT "", + C54 string DEFAULT "", + C55 string DEFAULT "", + C56 string DEFAULT "", + C57 string DEFAULT "", + C58 string DEFAULT "", + C59 string DEFAULT "", + C60 string DEFAULT "", + C61 string DEFAULT "", + C62 string DEFAULT "", + C63 string DEFAULT "", + C64 string DEFAULT "", + C65 string DEFAULT "", + C66 string DEFAULT "", + C67 string DEFAULT "", + C68 string DEFAULT "", + C69 string DEFAULT "", + C70 string DEFAULT "", + C71 string DEFAULT "", + C72 string DEFAULT "", + C73 string DEFAULT "", + C74 string DEFAULT "", + C75 string DEFAULT "", + C76 string DEFAULT "", + C77 string DEFAULT "", + C78 string DEFAULT "", + C79 string DEFAULT "", + C80 string DEFAULT "", + C81 string DEFAULT "", + C82 string DEFAULT "", + C83 string DEFAULT "", + C84 string DEFAULT "", + C85 string DEFAULT "", + C86 string DEFAULT "", + C87 string DEFAULT "", + C88 string DEFAULT "", + C89 string DEFAULT "", + C90 string DEFAULT "", + C91 string DEFAULT "", + C92 string DEFAULT "", + C93 string DEFAULT "", + C94 string DEFAULT "", + C95 string DEFAULT "", + C96 string DEFAULT "", + C97 string DEFAULT "", + C98 string DEFAULT "", + C99 string DEFAULT "", + );`); err != nil { + return err + } + + return tx.Commit() +} + +func issue99Fill(db *sql.DB) (int, error) { + tx, err := db.Begin() + if err != nil { + return -1, err + } + + sql := "INSERT INTO Node (" + strings.Join(fieldsIssue99, ",") + ") VALUES ($1, $2, $3, $4" + for i := range valuesIssue99 { + if i > 3 { + sql += ", $" + strconv.Itoa(i+1) + } + } + sql += ")" + + stmt, err := tx.Prepare(sql) + if err != nil { + return 0, err + } + + for i := 0; i < issue99RowsToInsert; i++ { + if _, err = stmt.Exec(valuesIssue99...); err != nil { + return 0, err + } + } + + return issue99RowsToInsert, tx.Commit() +} + +func testIssue99(tb testing.TB, db *sql.DB) int { + sum := 0 + for i := 0; i < issue99Cycles; i++ { + if err := createTablesIssue99(db); err != nil { + tb.Fatal(err) + } + + n2, err := issue99Fill(db) + if err != nil { + tb.Fatal(err) + } + + sum += n2 + } + return sum +} + +var benchmarkIssue99 sync.Once + +func BenchmarkIssue99(b *testing.B) { + if testing.Verbose() { + benchProlog(b) + benchmarkIssue99.Do(func() { + b.Logf(`1 op == (Re)create a 100+ column table, fill it with %d records. Repeat %d times. + +`, issue99RowsToInsert, issue99Cycles) + }) + } + RegisterMemDriver() + db, err := sql.Open("ql-mem", "issue99") + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + recs := 0 + for i := 0; i < b.N; i++ { + recs = testIssue99(b, db) + } + b.SetBytes(int64(recs) * benchScale) +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/benchcmp b/Godeps/_workspace/src/github.com/cznic/ql/benchcmp new file mode 100644 index 000000000..a0f7a50c0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/benchcmp @@ -0,0 +1 @@ +go test -test.run NONE -test.bench . -test.benchmem -timeout 1h | tee $1 diff --git a/Godeps/_workspace/src/github.com/cznic/ql/blob.go b/Godeps/_workspace/src/github.com/cznic/ql/blob.go new file mode 100644 index 000000000..2005b52c7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/blob.go @@ -0,0 +1,155 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "bytes" + "encoding/gob" + "math/big" + "sync" + "time" +) + +const shortBlob = 256 // bytes + +var ( + gobInitDuration = time.Duration(278) + gobInitInt = big.NewInt(42) + gobInitRat = big.NewRat(355, 113) + gobInitTime time.Time +) + +func init() { + var err error + if gobInitTime, err = time.ParseInLocation( + "Jan 2, 2006 at 3:04pm (MST)", + "Jul 9, 2012 at 5:02am (CEST)", + time.FixedZone("XYZ", 1234), + ); err != nil { + panic(err) + } + newGobCoder() +} + +type gobCoder struct { + buf bytes.Buffer + dec *gob.Decoder + enc *gob.Encoder + mu sync.Mutex +} + +func newGobCoder() (g *gobCoder) { + g = &gobCoder{} + g.enc = gob.NewEncoder(&g.buf) + if err := g.enc.Encode(gobInitInt); err != nil { + panic(err) + } + + if err := g.enc.Encode(gobInitRat); err != nil { + panic(err) + } + + if err := g.enc.Encode(gobInitTime); err != nil { + panic(err) + } + + if err := g.enc.Encode(gobInitDuration); err != nil { + panic(err) + } + + g.dec = gob.NewDecoder(&g.buf) + i := big.NewInt(0) + if err := g.dec.Decode(i); err != nil { + panic(err) + } + + r := big.NewRat(3, 5) + if err := g.dec.Decode(r); err != nil { + panic(err) + } + + t := time.Now() + if err := g.dec.Decode(&t); err != nil { + panic(err) + } + + var d time.Duration + if err := g.dec.Decode(&d); err != nil { + panic(err) + } + + return +} + +func isBlobType(v interface{}) (bool, Type) { + switch v.(type) { + case []byte: + return true, Blob + case *big.Int: + return true, BigInt + case *big.Rat: + return true, BigRat + case time.Time: + return true, Time + case time.Duration: + return true, Duration + default: + return false, -1 + } +} + +func (g *gobCoder) encode(v interface{}) (b []byte, err error) { + g.mu.Lock() + defer g.mu.Unlock() + + g.buf.Reset() + switch x := v.(type) { + case []byte: + return x, nil + case *big.Int: + err = g.enc.Encode(x) + case *big.Rat: + err = g.enc.Encode(x) + case time.Time: + err = g.enc.Encode(x) + case time.Duration: + err = g.enc.Encode(int64(x)) + default: + panic("internal error 002") + } + b = g.buf.Bytes() + return +} + +func (g *gobCoder) decode(b []byte, typ int) (v interface{}, err error) { + g.mu.Lock() + defer g.mu.Unlock() + + g.buf.Reset() + g.buf.Write(b) + switch typ { + case qBlob: + return b, nil + case qBigInt: + x := big.NewInt(0) + err = g.dec.Decode(&x) + v = x + case qBigRat: + x := big.NewRat(1, 1) + err = g.dec.Decode(&x) + v = x + case qTime: + var x time.Time + err = g.dec.Decode(&x) + v = x + case qDuration: + var x int64 + err = g.dec.Decode(&x) + v = time.Duration(x) + default: + panic("internal error 003") + } + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/btree.go b/Godeps/_workspace/src/github.com/cznic/ql/btree.go new file mode 100644 index 000000000..d1d6a558f --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/btree.go @@ -0,0 +1,725 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "io" +) + +const ( + kx = 128 //DONE benchmark tune this number if using custom key/value type(s). + kd = 64 //DONE benchmark tune this number if using custom key/value type(s). +) + +type ( + // cmp compares a and b. Return value is: + // + // < 0 if a < b + // 0 if a == b + // > 0 if a > b + // + cmp func(a, b []interface{}) int + + d struct { // data page + c int + d [2*kd + 1]de + n *d + p *d + } + + de struct { // d element + k []interface{} + v []interface{} + } + + enumerator struct { + err error + hit bool + i int + k []interface{} + q *d + t *tree + ver int64 + } + + // tree is a B+tree. + tree struct { + c int + cmp cmp + first *d + last *d + r interface{} + ver int64 + } + + xe struct { // x element + ch interface{} + sep *d + } + + x struct { // index page + c int + x [2*kx + 2]xe + } +) + +var ( // R/O zero values + zd d + zde de + zx x + zxe xe +) + +func clr(q interface{}) { + switch z := q.(type) { + case *x: + for i := 0; i <= z.c; i++ { // Ch0 Sep0 ... Chn-1 Sepn-1 Chn + clr(z.x[i].ch) + } + *z = zx // GC + case *d: + *z = zd // GC + } +} + +// -------------------------------------------------------------------------- x + +func newX(ch0 interface{}) *x { + r := &x{} + r.x[0].ch = ch0 + return r +} + +func (q *x) extract(i int) { + q.c-- + if i < q.c { + copy(q.x[i:], q.x[i+1:q.c+1]) + q.x[q.c].ch = q.x[q.c+1].ch + q.x[q.c].sep = nil // GC + q.x[q.c+1] = zxe // GC + } +} + +func (q *x) insert(i int, d *d, ch interface{}) *x { + c := q.c + if i < c { + q.x[c+1].ch = q.x[c].ch + copy(q.x[i+2:], q.x[i+1:c]) + q.x[i+1].sep = q.x[i].sep + } + c++ + q.c = c + q.x[i].sep = d + q.x[i+1].ch = ch + return q +} + +func (q *x) siblings(i int) (l, r *d) { + if i >= 0 { + if i > 0 { + l = q.x[i-1].ch.(*d) + } + if i < q.c { + r = q.x[i+1].ch.(*d) + } + } + return +} + +// -------------------------------------------------------------------------- d + +func (l *d) mvL(r *d, c int) { + copy(l.d[l.c:], r.d[:c]) + copy(r.d[:], r.d[c:r.c]) + l.c += c + r.c -= c +} + +func (l *d) mvR(r *d, c int) { + copy(r.d[c:], r.d[:r.c]) + copy(r.d[:c], l.d[l.c-c:]) + r.c += c + l.c -= c +} + +// ----------------------------------------------------------------------- tree + +// treeNew returns a newly created, empty tree. The compare function is used +// for key collation. +func treeNew(cmp cmp) *tree { + return &tree{cmp: cmp} +} + +// Clear removes all K/V pairs from the tree. +func (t *tree) Clear() { + if t.r == nil { + return + } + + clr(t.r) + t.c, t.first, t.last, t.r = 0, nil, nil, nil + t.ver++ +} + +func (t *tree) cat(p *x, q, r *d, pi int) { + t.ver++ + q.mvL(r, r.c) + if r.n != nil { + r.n.p = q + } else { + t.last = q + } + q.n = r.n + if p.c > 1 { + p.extract(pi) + p.x[pi].ch = q + } else { + t.r = q + } +} + +func (t *tree) catX(p, q, r *x, pi int) { + t.ver++ + q.x[q.c].sep = p.x[pi].sep + copy(q.x[q.c+1:], r.x[:r.c]) + q.c += r.c + 1 + q.x[q.c].ch = r.x[r.c].ch + if p.c > 1 { + p.c-- + pc := p.c + if pi < pc { + p.x[pi].sep = p.x[pi+1].sep + copy(p.x[pi+1:], p.x[pi+2:pc+1]) + p.x[pc].ch = p.x[pc+1].ch + p.x[pc].sep = nil // GC + p.x[pc+1].ch = nil // GC + } + return + } + + t.r = q +} + +//Delete removes the k's KV pair, if it exists, in which case Delete returns +//true. +func (t *tree) Delete(k []interface{}) (ok bool) { + pi := -1 + var p *x + q := t.r + if q == nil { + return + } + + for { + var i int + i, ok = t.find(q, k) + if ok { + switch z := q.(type) { + case *x: + dp := z.x[i].sep + switch { + case dp.c > kd: + t.extract(dp, 0) + default: + if z.c < kx && q != t.r { + t.underflowX(p, &z, pi, &i) + } + pi = i + 1 + p = z + q = z.x[pi].ch + ok = false + continue + } + case *d: + t.extract(z, i) + if z.c >= kd { + return + } + + if q != t.r { + t.underflow(p, z, pi) + } else if t.c == 0 { + t.Clear() + } + } + return + } + + switch z := q.(type) { + case *x: + if z.c < kx && q != t.r { + t.underflowX(p, &z, pi, &i) + } + pi = i + p = z + q = z.x[i].ch + case *d: + return + } + } +} + +func (t *tree) extract(q *d, i int) { // (r []interface{}) { + t.ver++ + //r = q.d[i].v // prepared for Extract + q.c-- + if i < q.c { + copy(q.d[i:], q.d[i+1:q.c+1]) + } + q.d[q.c] = zde // GC + t.c-- + return +} + +func (t *tree) find(q interface{}, k []interface{}) (i int, ok bool) { + var mk []interface{} + l := 0 + switch z := q.(type) { + case *x: + h := z.c - 1 + for l <= h { + m := (l + h) >> 1 + mk = z.x[m].sep.d[0].k + switch cmp := t.cmp(k, mk); { + case cmp > 0: + l = m + 1 + case cmp == 0: + return m, true + default: + h = m - 1 + } + } + case *d: + h := z.c - 1 + for l <= h { + m := (l + h) >> 1 + mk = z.d[m].k + switch cmp := t.cmp(k, mk); { + case cmp > 0: + l = m + 1 + case cmp == 0: + return m, true + default: + h = m - 1 + } + } + } + return l, false +} + +// First returns the first item of the tree in the key collating order, or +// (nil, nil) if the tree is empty. +func (t *tree) First() (k []interface{}, v []interface{}) { + if q := t.first; q != nil { + q := &q.d[0] + k, v = q.k, q.v + } + return +} + +// Get returns the value associated with k and true if it exists. Otherwise Get +// returns (nil, false). +func (t *tree) Get(k []interface{}) (v []interface{}, ok bool) { + q := t.r + if q == nil { + return + } + + for { + var i int + if i, ok = t.find(q, k); ok { + switch z := q.(type) { + case *x: + return z.x[i].sep.d[0].v, true + case *d: + return z.d[i].v, true + } + } + switch z := q.(type) { + case *x: + q = z.x[i].ch + default: + return + } + } +} + +func (t *tree) insert(q *d, i int, k []interface{}, v []interface{}) *d { + t.ver++ + c := q.c + if i < c { + copy(q.d[i+1:], q.d[i:c]) + } + c++ + q.c = c + q.d[i].k, q.d[i].v = k, v + t.c++ + return q +} + +// Last returns the last item of the tree in the key collating order, or (nil, +// nil) if the tree is empty. +func (t *tree) Last() (k []interface{}, v []interface{}) { + if q := t.last; q != nil { + q := &q.d[q.c-1] + k, v = q.k, q.v + } + return +} + +// Len returns the number of items in the tree. +func (t *tree) Len() int { + return t.c +} + +func (t *tree) overflow(p *x, q *d, pi, i int, k []interface{}, v []interface{}) { + t.ver++ + l, r := p.siblings(pi) + + if l != nil && l.c < 2*kd { + l.mvL(q, 1) + t.insert(q, i-1, k, v) + return + } + + if r != nil && r.c < 2*kd { + if i < 2*kd { + q.mvR(r, 1) + t.insert(q, i, k, v) + } else { + t.insert(r, 0, k, v) + } + return + } + + t.split(p, q, pi, i, k, v) +} + +// Seek returns an enumerator positioned on a an item such that k >= item's +// key. ok reports if k == item.key The enumerator's position is possibly +// after the last item in the tree. +func (t *tree) Seek(k []interface{}) (e *enumerator, ok bool) { + q := t.r + if q == nil { + e = &enumerator{nil, false, 0, k, nil, t, t.ver} + return + } + + for { + var i int + if i, ok = t.find(q, k); ok { + switch z := q.(type) { + case *x: + e = &enumerator{nil, ok, 0, k, z.x[i].sep, t, t.ver} + return + case *d: + e = &enumerator{nil, ok, i, k, z, t, t.ver} + return + } + } + switch z := q.(type) { + case *x: + q = z.x[i].ch + case *d: + e = &enumerator{nil, ok, i, k, z, t, t.ver} + return + } + } +} + +// SeekFirst returns an enumerator positioned on the first KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returned and e will be nil. +func (t *tree) SeekFirst() (e *enumerator, err error) { + q := t.first + if q == nil { + return nil, io.EOF + } + + return &enumerator{nil, true, 0, q.d[0].k, q, t, t.ver}, nil +} + +// SeekLast returns an enumerator positioned on the last KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returned and e will be nil. +func (t *tree) SeekLast() (e *enumerator, err error) { + q := t.last + if q == nil { + return nil, io.EOF + } + + return &enumerator{nil, true, q.c - 1, q.d[q.c-1].k, q, t, t.ver}, nil +} + +// Set sets the value associated with k. +func (t *tree) Set(k []interface{}, v []interface{}) { + pi := -1 + var p *x + q := t.r + if q != nil { + for { + i, ok := t.find(q, k) + if ok { + switch z := q.(type) { + case *x: + z.x[i].sep.d[0].v = v + case *d: + z.d[i].v = v + } + return + } + + switch z := q.(type) { + case *x: + if z.c > 2*kx { + t.splitX(p, &z, pi, &i) + } + pi = i + p = z + q = z.x[i].ch + case *d: + switch { + case z.c < 2*kd: + t.insert(z, i, k, v) + default: + t.overflow(p, z, pi, i, k, v) + } + return + } + } + } + + z := t.insert(&d{}, 0, k, v) + t.r, t.first, t.last = z, z, z + return +} + +func (t *tree) split(p *x, q *d, pi, i int, k []interface{}, v []interface{}) { + t.ver++ + r := &d{} + if q.n != nil { + r.n = q.n + r.n.p = r + } else { + t.last = r + } + q.n = r + r.p = q + + copy(r.d[:], q.d[kd:2*kd]) + for i := range q.d[kd:] { + q.d[kd+i] = zde + } + q.c = kd + r.c = kd + if pi >= 0 { + p.insert(pi, r, r) + } else { + t.r = newX(q).insert(0, r, r) + } + if i > kd { + t.insert(r, i-kd, k, v) + return + } + + t.insert(q, i, k, v) +} + +func (t *tree) splitX(p *x, pp **x, pi int, i *int) { + t.ver++ + q := *pp + r := &x{} + copy(r.x[:], q.x[kx+1:]) + q.c = kx + r.c = kx + if pi >= 0 { + p.insert(pi, q.x[kx].sep, r) + } else { + t.r = newX(q).insert(0, q.x[kx].sep, r) + } + q.x[kx].sep = nil + for i := range q.x[kx+1:] { + q.x[kx+i+1] = zxe + } + if *i > kx { + *pp = r + *i -= kx + 1 + } +} + +func (t *tree) underflow(p *x, q *d, pi int) { + t.ver++ + l, r := p.siblings(pi) + + if l != nil && l.c+q.c >= 2*kd { + l.mvR(q, 1) + } else if r != nil && q.c+r.c >= 2*kd { + q.mvL(r, 1) + r.d[r.c] = zde // GC + } else if l != nil { + t.cat(p, l, q, pi-1) + } else { + t.cat(p, q, r, pi) + } +} + +func (t *tree) underflowX(p *x, pp **x, pi int, i *int) { + t.ver++ + var l, r *x + q := *pp + + if pi >= 0 { + if pi > 0 { + l = p.x[pi-1].ch.(*x) + } + if pi < p.c { + r = p.x[pi+1].ch.(*x) + } + } + + if l != nil && l.c > kx { + q.x[q.c+1].ch = q.x[q.c].ch + copy(q.x[1:], q.x[:q.c]) + q.x[0].ch = l.x[l.c].ch + q.x[0].sep = p.x[pi-1].sep + q.c++ + *i++ + l.c-- + p.x[pi-1].sep = l.x[l.c].sep + return + } + + if r != nil && r.c > kx { + q.x[q.c].sep = p.x[pi].sep + q.c++ + q.x[q.c].ch = r.x[0].ch + p.x[pi].sep = r.x[0].sep + copy(r.x[:], r.x[1:r.c]) + r.c-- + rc := r.c + r.x[rc].ch = r.x[rc+1].ch + r.x[rc].sep = nil + r.x[rc+1].ch = nil + return + } + + if l != nil { + *i += l.c + 1 + t.catX(p, l, q, pi-1) + *pp = l + return + } + + t.catX(p, q, r, pi) +} + +// ----------------------------------------------------------------- enumerator + +// Next returns the currently enumerated item, if it exists and moves to the +// next item in the key collation order. If there is no item to return, err == +// io.EOF is returned. +func (e *enumerator) Next() (k []interface{}, v []interface{}, err error) { + if err = e.err; err != nil { + return + } + + if e.ver != e.t.ver { + f, hit := e.t.Seek(e.k) + if !e.hit && hit { + if err = f.next(); err != nil { + return + } + } + + *e = *f + } + if e.q == nil { + e.err, err = io.EOF, io.EOF + return + } + + if e.i >= e.q.c { + if err = e.next(); err != nil { + return + } + } + + i := e.q.d[e.i] + k, v = i.k, i.v + e.k, e.hit = k, false + e.next() + return +} + +func (e *enumerator) next() error { + if e.q == nil { + e.err = io.EOF + return io.EOF + } + + switch { + case e.i < e.q.c-1: + e.i++ + default: + if e.q, e.i = e.q.n, 0; e.q == nil { + e.err = io.EOF + } + } + return e.err +} + +// Prev returns the currently enumerated item, if it exists and moves to the +// previous item in the key collation order. If there is no item to return, err +// == io.EOF is returned. +func (e *enumerator) Prev() (k []interface{}, v []interface{}, err error) { + if err = e.err; err != nil { + return + } + + if e.ver != e.t.ver { + f, hit := e.t.Seek(e.k) + if !e.hit && hit { + if err = f.prev(); err != nil { + return + } + } + + *e = *f + } + if e.q == nil { + e.err, err = io.EOF, io.EOF + return + } + + if e.i >= e.q.c { + if err = e.next(); err != nil { + return + } + } + + i := e.q.d[e.i] + k, v = i.k, i.v + e.k, e.hit = k, false + e.prev() + return +} + +func (e *enumerator) prev() error { + if e.q == nil { + e.err = io.EOF + return io.EOF + } + + switch { + case e.i > 0: + e.i-- + default: + if e.q = e.q.p; e.q == nil { + e.err = io.EOF + break + } + + e.i = e.q.c - 1 + } + return e.err +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/builtin.go b/Godeps/_workspace/src/github.com/cznic/ql/builtin.go new file mode 100644 index 000000000..1c4bc1c1a --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/builtin.go @@ -0,0 +1,991 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "fmt" + "math/rand" + "reflect" + "strconv" + "strings" + "time" +) + +//TODO agg bigint, bigrat, time, duration + +var builtin = map[string]struct { + f func([]interface{}, map[interface{}]interface{}) (interface{}, error) + minArgs int + maxArgs int + isStatic bool + isAggregate bool +}{ + "__testBlob": {builtinTestBlob, 1, 1, true, false}, + "__testString": {builtinTestString, 1, 1, true, false}, + "avg": {builtinAvg, 1, 1, false, true}, + "complex": {builtinComplex, 2, 2, true, false}, + "contains": {builtinContains, 2, 2, true, false}, + "count": {builtinCount, 0, 1, false, true}, + "date": {builtinDate, 8, 8, true, false}, + "day": {builtinDay, 1, 1, true, false}, + "formatTime": {builtinFormatTime, 2, 2, true, false}, + "formatFloat": {builtinFormatFloat, 1, 4, true, false}, + "formatInt": {builtinFormatInt, 1, 2, true, false}, + "hasPrefix": {builtinHasPrefix, 2, 2, true, false}, + "hasSuffix": {builtinHasSuffix, 2, 2, true, false}, + "hour": {builtinHour, 1, 1, true, false}, + "hours": {builtinHours, 1, 1, true, false}, + "id": {builtinID, 0, 1, false, false}, + "imag": {builtinImag, 1, 1, true, false}, + "len": {builtinLen, 1, 1, true, false}, + "max": {builtinMax, 1, 1, false, true}, + "min": {builtinMin, 1, 1, false, true}, + "minute": {builtinMinute, 1, 1, true, false}, + "minutes": {builtinMinutes, 1, 1, true, false}, + "month": {builtinMonth, 1, 1, true, false}, + "nanosecond": {builtinNanosecond, 1, 1, true, false}, + "nanoseconds": {builtinNanoseconds, 1, 1, true, false}, + "now": {builtinNow, 0, 0, false, false}, + "parseTime": {builtinParseTime, 2, 2, true, false}, + "real": {builtinReal, 1, 1, true, false}, + "second": {builtinSecond, 1, 1, true, false}, + "seconds": {builtinSeconds, 1, 1, true, false}, + "since": {builtinSince, 1, 1, false, false}, + "sum": {builtinSum, 1, 1, false, true}, + "timeIn": {builtinTimeIn, 2, 2, true, false}, + "weekday": {builtinWeekday, 1, 1, true, false}, + "year": {builtinYear, 1, 1, true, false}, + "yearDay": {builtinYearday, 1, 1, true, false}, +} + +func badNArgs(min int, s string, arg []interface{}) error { + a := []string{} + for _, v := range arg { + a = append(a, fmt.Sprintf("%v", v)) + } + switch len(arg) < min { + case true: + return fmt.Errorf("missing argument to %s(%s)", s, strings.Join(a, ", ")) + default: //case false: + return fmt.Errorf("too many arguments to %s(%s)", s, strings.Join(a, ", ")) + } +} + +func invArg(arg interface{}, s string) error { + return fmt.Errorf("invalid argument %v (type %T) for %s", arg, arg, s) +} + +func builtinTestBlob(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + n, err := intExpr(arg[0]) + if err != nil { + return nil, err + } + + rng := rand.New(rand.NewSource(n)) + b := make([]byte, n) + for i := range b { + b[i] = byte(rng.Int()) + } + return b, nil +} + +func builtinTestString(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + n, err := intExpr(arg[0]) + if err != nil { + return nil, err + } + + rng := rand.New(rand.NewSource(n)) + b := make([]byte, n) + for i := range b { + b[i] = byte(rng.Int()) + } + return string(b), nil +} + +func builtinAvg(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + type avg struct { + sum interface{} + n uint64 + } + + if _, ok := ctx["$agg0"]; ok { + return + } + + fn := ctx["$fn"] + if _, ok := ctx["$agg"]; ok { + data, ok := ctx[fn].(avg) + if !ok { + return + } + + switch x := data.sum.(type) { + case complex64: + return complex64(complex128(x) / complex(float64(data.n), 0)), nil + case complex128: + return complex64(complex128(x) / complex(float64(data.n), 0)), nil + case float32: + return float32(float64(x) / float64(data.n)), nil + case float64: + return float64(x) / float64(data.n), nil + case int8: + return int8(int64(x) / int64(data.n)), nil + case int16: + return int16(int64(x) / int64(data.n)), nil + case int32: + return int32(int64(x) / int64(data.n)), nil + case int64: + return int64(int64(x) / int64(data.n)), nil + case uint8: + return uint8(uint64(x) / data.n), nil + case uint16: + return uint16(uint64(x) / data.n), nil + case uint32: + return uint32(uint64(x) / data.n), nil + case uint64: + return uint64(uint64(x) / data.n), nil + } + + } + + data, _ := ctx[fn].(avg) + y := arg[0] + if y == nil { + return + } + + switch x := data.sum.(type) { + case nil: + switch y := y.(type) { + case float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64: + data = avg{y, 0} + default: + return nil, fmt.Errorf("avg: cannot accept %v (value if type %T)", y, y) + } + case complex64: + data.sum = x + y.(complex64) + case complex128: + data.sum = x + y.(complex128) + case float32: + data.sum = x + y.(float32) + case float64: + data.sum = x + y.(float64) + case int8: + data.sum = x + y.(int8) + case int16: + data.sum = x + y.(int16) + case int32: + data.sum = x + y.(int32) + case int64: + data.sum = x + y.(int64) + case uint8: + data.sum = x + y.(uint8) + case uint16: + data.sum = x + y.(uint16) + case uint32: + data.sum = x + y.(uint32) + case uint64: + data.sum = x + y.(uint64) + } + data.n++ + ctx[fn] = data + return +} + +func builtinComplex(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) { + re, im := arg[0], arg[1] + if re == nil || im == nil { + return nil, nil + } + + re, im = coerce(re, im) + if reflect.TypeOf(re) != reflect.TypeOf(im) { + return nil, fmt.Errorf("complex(%T(%#v), %T(%#v)): invalid types", re, re, im, im) + } + + switch re := re.(type) { + case idealFloat: + return idealComplex(complex(float64(re), float64(im.(idealFloat)))), nil + case idealInt: + return idealComplex(complex(float64(re), float64(im.(idealInt)))), nil + case idealRune: + return idealComplex(complex(float64(re), float64(im.(idealRune)))), nil + case idealUint: + return idealComplex(complex(float64(re), float64(im.(idealUint)))), nil + case float32: + return complex(float32(re), im.(float32)), nil + case float64: + return complex(float64(re), im.(float64)), nil + case int8: + return complex(float64(re), float64(im.(int8))), nil + case int16: + return complex(float64(re), float64(im.(int16))), nil + case int32: + return complex(float64(re), float64(im.(int32))), nil + case int64: + return complex(float64(re), float64(im.(int64))), nil + case uint8: + return complex(float64(re), float64(im.(uint8))), nil + case uint16: + return complex(float64(re), float64(im.(uint16))), nil + case uint32: + return complex(float64(re), float64(im.(uint32))), nil + case uint64: + return complex(float64(re), float64(im.(uint64))), nil + default: + return nil, invArg(re, "complex") + } +} + +func builtinContains(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) { + switch s := arg[0].(type) { + case nil: + return nil, nil + case string: + switch chars := arg[1].(type) { + case nil: + return nil, nil + case string: + return strings.Contains(s, chars), nil + default: + return nil, invArg(chars, "string") + } + default: + return nil, invArg(s, "string") + } +} + +func builtinCount(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + if _, ok := ctx["$agg0"]; ok { + return int64(0), nil + } + + fn := ctx["$fn"] + if _, ok := ctx["$agg"]; ok { + return ctx[fn].(int64), nil + } + + n, _ := ctx[fn].(int64) + switch len(arg) { + case 0: + n++ + case 1: + if arg[0] != nil { + n++ + } + default: + panic("internal error 067") + } + ctx[fn] = n + return +} + +func builtinDate(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) { + for i, v := range arg { + switch i { + case 7: + switch x := v.(type) { + case string: + default: + return nil, invArg(x, "date") + } + default: + switch x := v.(type) { + case int64: + case idealInt: + arg[i] = int64(x) + default: + return nil, invArg(x, "date") + } + } + } + + sloc := arg[7].(string) + loc := time.Local + switch sloc { + case "local": + default: + loc, err = time.LoadLocation(sloc) + if err != nil { + return + } + } + + return time.Date( + int(arg[0].(int64)), + time.Month(arg[1].(int64)), + int(arg[2].(int64)), + int(arg[3].(int64)), + int(arg[4].(int64)), + int(arg[5].(int64)), + int(arg[6].(int64)), + loc, + ), nil +} + +func builtinLen(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case string: + return int64(len(x)), nil + default: + return nil, invArg(x, "len") + } +} + +func builtinDay(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return int64(x.Day()), nil + default: + return nil, invArg(x, "day") + } +} + +func builtinFormatTime(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + switch y := arg[1].(type) { + case nil: + return nil, nil + case string: + return x.Format(y), nil + default: + return nil, invArg(y, "formatTime") + } + default: + return nil, invArg(x, "formatTime") + } +} + +func builtinFormatFloat(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + var val float64 + var fmt byte = 'g' + + prec := -1 + bitSize := 64 + + switch x := arg[0].(type) { + case nil: + return nil, nil + case float32: + val = float64(x) + bitSize = 32 + case float64: + val = x + default: + return nil, invArg(x, "formatFloat") + } + + switch len(arg) { + case 4: + arg3 := coerce1(arg[3], int64(0)) + switch y := arg3.(type) { + case nil: + return nil, nil + case int64: + bitSize = int(y) + default: + return nil, invArg(y, "formatFloat") + } + fallthrough + case 3: + arg2 := coerce1(arg[2], int64(0)) + switch y := arg2.(type) { + case nil: + return nil, nil + case int64: + prec = int(y) + default: + return nil, invArg(y, "formatFloat") + } + fallthrough + case 2: + arg1 := coerce1(arg[1], byte(0)) + switch y := arg1.(type) { + case nil: + return nil, nil + case byte: + fmt = y + default: + return nil, invArg(y, "formatFloat") + } + } + + return strconv.FormatFloat(val, fmt, prec, bitSize), nil +} + +func builtinFormatInt(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + var intVal int64 + var uintVal uint64 + + uintType := false + base := 10 + + switch x := arg[0].(type) { + case nil: + return nil, nil + case int8: + intVal = int64(x) + case int16: + intVal = int64(x) + case int32: + intVal = int64(x) + case int64: + intVal = x + case uint8: + uintType = true + uintVal = uint64(x) + case uint16: + uintType = true + uintVal = uint64(x) + case uint32: + uintType = true + uintVal = uint64(x) + case uint64: + uintType = true + uintVal = x + default: + return nil, invArg(x, "formatInt") + } + + switch len(arg) { + case 2: + arg1 := coerce1(arg[1], int64(0)) + switch y := arg1.(type) { + case nil: + return nil, nil + case int64: + base = int(y) + default: + return nil, invArg(y, "formatInt") + } + } + + if uintType { + return strconv.FormatUint(uintVal, base), nil + } + + return strconv.FormatInt(intVal, base), nil +} + +func builtinHasPrefix(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) { + switch s := arg[0].(type) { + case nil: + return nil, nil + case string: + switch prefix := arg[1].(type) { + case nil: + return nil, nil + case string: + return strings.HasPrefix(s, prefix), nil + default: + return nil, invArg(prefix, "string") + } + default: + return nil, invArg(s, "string") + } +} + +func builtinHasSuffix(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) { + switch s := arg[0].(type) { + case nil: + return nil, nil + case string: + switch suffix := arg[1].(type) { + case nil: + return nil, nil + case string: + return strings.HasSuffix(s, suffix), nil + default: + return nil, invArg(suffix, "string") + } + default: + return nil, invArg(s, "string") + } +} + +func builtinHour(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return int64(x.Hour()), nil + default: + return nil, invArg(x, "hour") + } +} + +func builtinHours(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Duration: + return x.Hours(), nil + default: + return nil, invArg(x, "hours") + } +} + +func builtinID(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := ctx["$id"].(type) { + case map[string]interface{}: + if len(arg) == 0 { + return nil, nil + } + + tab := arg[0].(*ident) + id, ok := x[tab.s] + if !ok { + return nil, fmt.Errorf("value not available: id(%s)", tab) + } + + if _, ok := id.(int64); ok { + return id, nil + } + + return nil, fmt.Errorf("value not available: id(%s)", tab) + case int64: + return x, nil + default: + return nil, nil + } +} + +func builtinImag(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case idealComplex: + return imag(x), nil + case complex64: + return imag(x), nil + case complex128: + return imag(x), nil + default: + return nil, invArg(x, "imag") + } +} + +func builtinMax(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + if _, ok := ctx["$agg0"]; ok { + return + } + + fn := ctx["$fn"] + if _, ok := ctx["$agg"]; ok { + if v, ok = ctx[fn]; ok { + return + } + + return nil, nil + } + + max := ctx[fn] + y := arg[0] + if y == nil { + return + } + switch x := max.(type) { + case nil: + switch y := y.(type) { + case float32, float64, string, int8, int16, int32, int64, uint8, uint16, uint32, uint64, time.Time: + max = y + default: + return nil, fmt.Errorf("max: cannot accept %v (value if type %T)", y, y) + } + case float32: + if y := y.(float32); y > x { + max = y + } + case float64: + if y := y.(float64); y > x { + max = y + } + case string: + if y := y.(string); y > x { + max = y + } + case int8: + if y := y.(int8); y > x { + max = y + } + case int16: + if y := y.(int16); y > x { + max = y + } + case int32: + if y := y.(int32); y > x { + max = y + } + case int64: + if y := y.(int64); y > x { + max = y + } + case uint8: + if y := y.(uint8); y > x { + max = y + } + case uint16: + if y := y.(uint16); y > x { + max = y + } + case uint32: + if y := y.(uint32); y > x { + max = y + } + case uint64: + if y := y.(uint64); y > x { + max = y + } + case time.Time: + if y := y.(time.Time); y.After(x) { + max = y + } + } + ctx[fn] = max + return +} + +func builtinMin(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + if _, ok := ctx["$agg0"]; ok { + return + } + + fn := ctx["$fn"] + if _, ok := ctx["$agg"]; ok { + if v, ok = ctx[fn]; ok { + return + } + + return nil, nil + } + + min := ctx[fn] + y := arg[0] + if y == nil { + return + } + switch x := min.(type) { + case nil: + switch y := y.(type) { + case float32, float64, string, int8, int16, int32, int64, uint8, uint16, uint32, uint64, time.Time: + min = y + default: + return nil, fmt.Errorf("min: cannot accept %v (value if type %T)", y, y) + } + case float32: + if y := y.(float32); y < x { + min = y + } + case float64: + if y := y.(float64); y < x { + min = y + } + case string: + if y := y.(string); y < x { + min = y + } + case int8: + if y := y.(int8); y < x { + min = y + } + case int16: + if y := y.(int16); y < x { + min = y + } + case int32: + if y := y.(int32); y < x { + min = y + } + case int64: + if y := y.(int64); y < x { + min = y + } + case uint8: + if y := y.(uint8); y < x { + min = y + } + case uint16: + if y := y.(uint16); y < x { + min = y + } + case uint32: + if y := y.(uint32); y < x { + min = y + } + case uint64: + if y := y.(uint64); y < x { + min = y + } + case time.Time: + if y := y.(time.Time); y.Before(x) { + min = y + } + } + ctx[fn] = min + return +} + +func builtinMinute(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return int64(x.Minute()), nil + default: + return nil, invArg(x, "minute") + } +} + +func builtinMinutes(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Duration: + return x.Minutes(), nil + default: + return nil, invArg(x, "minutes") + } +} + +func builtinMonth(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return int64(x.Month()), nil + default: + return nil, invArg(x, "month") + } +} + +func builtinNanosecond(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return int64(x.Nanosecond()), nil + default: + return nil, invArg(x, "nanosecond") + } +} + +func builtinNanoseconds(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Duration: + return x.Nanoseconds(), nil + default: + return nil, invArg(x, "nanoseconds") + } +} + +func builtinNow(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + return time.Now(), nil +} + +func builtinParseTime(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + var a [2]string + for i, v := range arg { + switch x := v.(type) { + case nil: + return nil, nil + case string: + a[i] = x + default: + return nil, invArg(x, "parseTime") + } + } + + t, err := time.Parse(a[0], a[1]) + if err != nil { + return nil, err + } + + ls := t.Location().String() + if ls == "UTC" { + return t, nil + } + + l, err := time.LoadLocation(ls) + if err != nil { + return t, nil + } + + return time.ParseInLocation(a[0], a[1], l) +} + +func builtinReal(arg []interface{}, _ map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case idealComplex: + return real(x), nil + case complex64: + return real(x), nil + case complex128: + return real(x), nil + default: + return nil, invArg(x, "real") + } +} + +func builtinSecond(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return int64(x.Second()), nil + default: + return nil, invArg(x, "second") + } +} + +func builtinSeconds(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Duration: + return x.Seconds(), nil + default: + return nil, invArg(x, "seconds") + } +} + +func builtinSince(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return time.Since(x), nil + default: + return nil, invArg(x, "since") + } +} + +func builtinSum(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + if _, ok := ctx["$agg0"]; ok { + return + } + + fn := ctx["$fn"] + if _, ok := ctx["$agg"]; ok { + if v, ok = ctx[fn]; ok { + return + } + + return nil, nil + } + + sum := ctx[fn] + y := arg[0] + if y == nil { + return + } + switch x := sum.(type) { + case nil: + switch y := y.(type) { + case complex64, complex128, float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64: + sum = y + default: + return nil, fmt.Errorf("sum: cannot accept %v (value if type %T)", y, y) + } + case complex64: + sum = x + y.(complex64) + case complex128: + sum = x + y.(complex128) + case float32: + sum = x + y.(float32) + case float64: + sum = x + y.(float64) + case int8: + sum = x + y.(int8) + case int16: + sum = x + y.(int16) + case int32: + sum = x + y.(int32) + case int64: + sum = x + y.(int64) + case uint8: + sum = x + y.(uint8) + case uint16: + sum = x + y.(uint16) + case uint32: + sum = x + y.(uint32) + case uint64: + sum = x + y.(uint64) + } + ctx[fn] = sum + return +} + +func builtinTimeIn(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + switch y := arg[1].(type) { + case nil: + return nil, nil + case string: + loc := time.Local + switch y { + case "local": + default: + loc, err = time.LoadLocation(y) + if err != nil { + return + } + } + + return x.In(loc), nil + default: + return nil, invArg(x, "timeIn") + } + default: + return nil, invArg(x, "timeIn") + } +} + +func builtinWeekday(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return int64(x.Weekday()), nil + default: + return nil, invArg(x, "weekday") + } +} + +func builtinYear(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return int64(x.Year()), nil + default: + return nil, invArg(x, "year") + } +} + +func builtinYearday(arg []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { + switch x := arg[0].(type) { + case nil: + return nil, nil + case time.Time: + return int64(x.YearDay()), nil + default: + return nil, invArg(x, "yearDay") + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/coerce.go b/Godeps/_workspace/src/github.com/cznic/ql/coerce.go new file mode 100644 index 000000000..368d5b389 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/coerce.go @@ -0,0 +1,290 @@ +// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// CAUTION: This file was generated automatically by +// +// $ go run helper -o coerce.go +// +// DO NOT EDIT! + +package ql + +import ( + "math" + "math/big" + "reflect" + "time" +) + +func coerce(a, b interface{}) (x, y interface{}) { + if reflect.TypeOf(a) == reflect.TypeOf(b) { + return a, b + } + + switch a.(type) { + case idealComplex, idealFloat, idealInt, idealRune, idealUint: + switch b.(type) { + case idealComplex, idealFloat, idealInt, idealRune, idealUint: + x, y = coerce1(a, b), b + if reflect.TypeOf(x) == reflect.TypeOf(y) { + return + } + + return a, coerce1(b, a) + default: + return coerce1(a, b), b + } + default: + switch b.(type) { + case idealComplex, idealFloat, idealInt, idealRune, idealUint: + return a, coerce1(b, a) + default: + return a, b + } + } +} + +func coerce1(inVal, otherVal interface{}) (coercedInVal interface{}) { + coercedInVal = inVal + if otherVal == nil { + return + } + + switch x := inVal.(type) { + case nil: + return + case idealComplex: + switch otherVal.(type) { + //case idealComplex: + //case idealFloat: + //case idealInt: + //case idealRune: + //case idealUint: + //case bool: + case complex64: + return complex64(x) + case complex128: + return complex128(x) + //case float32: + //case float64: + //case int8: + //case int16: + //case int32: + //case int64: + //case string: + //case uint8: + //case uint16: + //case uint32: + //case uint64: + //case *big.Int: + //case *big.Rat: + //case time.Time: + //case time.Duration: + } + case idealFloat: + switch otherVal.(type) { + case idealComplex: + return idealComplex(complex(float64(x), 0)) + case idealFloat: + return idealFloat(float64(x)) + //case idealInt: + //case idealRune: + //case idealUint: + //case bool: + case complex64: + return complex64(complex(float32(x), 0)) + case complex128: + return complex128(complex(float64(x), 0)) + case float32: + return float32(float64(x)) + case float64: + return float64(float64(x)) + //case int8: + //case int16: + //case int32: + //case int64: + //case string: + //case uint8: + //case uint16: + //case uint32: + //case uint64: + //case *big.Int: + case *big.Rat: + return big.NewRat(1, 1).SetFloat64(float64(x)) + //case time.Time: + //case time.Duration: + } + case idealInt: + switch otherVal.(type) { + case idealComplex: + return idealComplex(complex(float64(x), 0)) + case idealFloat: + return idealFloat(int64(x)) + case idealInt: + return idealInt(int64(x)) + //case idealRune: + case idealUint: + if x >= 0 { + return idealUint(int64(x)) + } + //case bool: + case complex64: + return complex64(complex(float32(x), 0)) + case complex128: + return complex128(complex(float64(x), 0)) + case float32: + return float32(int64(x)) + case float64: + return float64(int64(x)) + case int8: + if x >= math.MinInt8 && x <= math.MaxInt8 { + return int8(int64(x)) + } + case int16: + if x >= math.MinInt16 && x <= math.MaxInt16 { + return int16(int64(x)) + } + case int32: + if x >= math.MinInt32 && x <= math.MaxInt32 { + return int32(int64(x)) + } + case int64: + return int64(int64(x)) + //case string: + case uint8: + if x >= 0 && x <= math.MaxUint8 { + return uint8(int64(x)) + } + case uint16: + if x >= 0 && x <= math.MaxUint16 { + return uint16(int64(x)) + } + case uint32: + if x >= 0 && x <= math.MaxUint32 { + return uint32(int64(x)) + } + case uint64: + if x >= 0 { + return uint64(int64(x)) + } + case *big.Int: + return big.NewInt(int64(x)) + case *big.Rat: + return big.NewRat(1, 1).SetInt64(int64(x)) + //case time.Time: + case time.Duration: + return time.Duration(int64(x)) + } + case idealRune: + switch otherVal.(type) { + case idealComplex: + return idealComplex(complex(float64(x), 0)) + case idealFloat: + return idealFloat(int64(x)) + case idealInt: + return idealInt(int64(x)) + case idealRune: + return idealRune(int64(x)) + case idealUint: + return idealUint(int64(x)) + //case bool: + case complex64: + return complex64(complex(float32(x), 0)) + case complex128: + return complex128(complex(float64(x), 0)) + case float32: + return float32(int64(x)) + case float64: + return float64(int64(x)) + case int8: + return int8(int64(x)) + case int16: + return int16(int64(x)) + case int32: + return int32(int64(x)) + case int64: + return int64(int64(x)) + //case string: + case uint8: + return uint8(int64(x)) + case uint16: + return uint16(int64(x)) + case uint32: + return uint32(int64(x)) + case uint64: + return uint64(int64(x)) + case *big.Int: + return big.NewInt(int64(x)) + case *big.Rat: + return big.NewRat(1, 1).SetInt64(int64(x)) + //case time.Time: + case time.Duration: + return time.Duration(int64(x)) + } + case idealUint: + switch otherVal.(type) { + case idealComplex: + return idealComplex(complex(float64(x), 0)) + case idealFloat: + return idealFloat(uint64(x)) + case idealInt: + if x <= math.MaxInt64 { + return idealInt(int64(x)) + } + //case idealRune: + case idealUint: + return idealUint(uint64(x)) + //case bool: + case complex64: + return complex64(complex(float32(x), 0)) + case complex128: + return complex128(complex(float64(x), 0)) + case float32: + return float32(uint64(x)) + case float64: + return float64(uint64(x)) + case int8: + if x <= math.MaxInt8 { + return int8(int64(x)) + } + case int16: + if x <= math.MaxInt16 { + return int16(int64(x)) + } + case int32: + if x <= math.MaxInt32 { + return int32(int64(x)) + } + case int64: + if x <= math.MaxInt64 { + return int64(int64(x)) + } + //case string: + case uint8: + if x >= 0 && x <= math.MaxUint8 { + return uint8(int64(x)) + } + case uint16: + if x >= 0 && x <= math.MaxUint16 { + return uint16(int64(x)) + } + case uint32: + if x >= 0 && x <= math.MaxUint32 { + return uint32(int64(x)) + } + case uint64: + return uint64(uint64(x)) + case *big.Int: + return big.NewInt(0).SetUint64(uint64(x)) + case *big.Rat: + return big.NewRat(1, 1).SetInt(big.NewInt(0).SetUint64(uint64(x))) + //case time.Time: + case time.Duration: + if x <= math.MaxInt64 { + return time.Duration(int64(x)) + } + } + } + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/design/doc.go b/Godeps/_workspace/src/github.com/cznic/ql/design/doc.go new file mode 100644 index 000000000..7e6718cfd --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/design/doc.go @@ -0,0 +1,298 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + +Package design describes some of the data structures used in QL. + +Handles + +A handle is a 7 byte "pointer" to a block in the DB[0]. + +Scalar encoding + +Encoding of so called "scalars" provided by [1]. Unless specified otherwise, +all values discussed below are scalars, encoded scalars or encoding of scalar +arrays. + +Database root + +DB root is a 1-scalar found at a fixed handle (#1). + + +---+------+--------+-----------------------+ + | # | Name | Type | Description | + +---+------+--------+-----------------------+ + | 0 | head | handle | First table meta data | + +---+------+--------+-----------------------+ + +Head is the head of a single linked list of table of meta data. It's zero if +there are no tables in the DB. + +Table meta data + +Table meta data are a 6-scalar. + + +---+---------+--------+--------------------------+ + | # | Name | Type | Description | + +---+---------+--------+--------------------------+ + | 0 | next | handle | Next table meta data. | + | 1 | scols | string | Column defintitions | + | 2 | hhead | handle | -> head -> first record | + | 3 | name | string | Table name | + | 4 | indices | string | Index definitions | + | 5 | hxroots | handle | Index B+Trees roots list | + +---+---------+--------+--------------------------+ + +Fields #4 and #5 are optional for backward compatibility with existing +databases. OTOH, forward compatibility will not work. Once any indices are +created using a newer QL version the older versions of QL, expecting only 4 +fields of meta data will not be able to use the DB. That's the intended +behavior because the older versions of QL cannot update the indexes, which can +break queries runned by the newer QL version which expect indices to be always +actualized on any table-with-indices mutation. + +The handle of the next table meta data is in the field #0 (next). If there is +no next table meta data, the field is zero. Names and types of table columns +are stored in field #1 (scols). A single field is described by concatenating a +type tag and the column name. The type tags are + + bool 'b' + complex64 'c' + complex128 'd' + float32 'f' + float64 'g', alias float + int8 'i' + int16 'j' + int32 'k' + int64 'l', alias int + string 's' + uint8 'u', alias byte + uint16 'v' + uint32 'w' + uint64 'x', alias uint + bigInt 'I' + bigRat 'R' + blob 'B' + duration 'D' + time 'T' + +The scols value is the above described encoded fields joined using "|". For +example + + CREATE TABLE t (Foo bool, Bar string, Baz float); + +This statement adds a table meta data with scols + + "bFool|sBar|gBaz" + +Columns can be dropped from a table + + ALTER TABLE t DROP COLUMN Bar; + +This "erases" the field info in scols, so the value becomes + + "bFool||gBaz" + +Colums can be added to a table + + ALTER TABLE t ADD Count uint; + +New fields are always added to the end of scols + + "bFool||gBaz|xCount" + +Index of a field in strings.Split(scols, "|") is the index of the field in a +table record. The above discussed rules for column dropping and column adding +allow for schema evolution without a need to reshape any existing table data. +Dropped columns are left where they are and new records insert nil in their +place. The encoded nil is one byte. Added columns, when not present in +preexisting records are returned as nil values. If the overhead of dropped +columns becomes an issue and there's time/space and memory enough to move the +records of a table around: + + BEGIN TRANSACTION; + CREATE TABLE new (column definitions); + INSERT INTO new SELECT * FROM old; + DROP TABLE old; + CREATE TABLE old (column definitions); + INSERT INTO old SELECT * FROM new; + DROP TABLE new; + END TRANSACTION; + +This is not very time/space effective and for Big Data it can cause an OOM +because transactions are limited by memory resources available to the process. +Perhaps a method and/or QL statement to do this in-place should be added +(MAYBE consider adopting MySQL's OPTIMIZE TABLE syntax). + +Field #2 (hhead) is a handle to a head of table records, i.e. not a handle to +the first record in the table. It is thus always non zero even for a table +having no records. The reason for this "double pointer" schema is to enable +adding (linking) a new record by updating a single value of the (hhead pointing +to) head. + + tableMeta.hhead -> head -> firstTableRecord + +The table name is stored in field #3 (name). + +Indices + +Consider an index named N, indexing column named C. The encoding of this +particular index is a string "N". is a string "n" for non unique +indices and "u" for unique indices. There is this index information for the +index possibly indexing the record id() and for all other columns of scols. +Where the column is not indexed, the index info is an empty string. Infos for +all indexes are joined with "|". For example + + BEGIN TRANSACTION; + CREATE TABLE t (Foo int, Bar bool, Baz string); + CREATE INDEX X ON t (Baz); + CREATE UNIQUE INDEX Y ON t (Foo); + COMMIT; + +The values of fields #1 and #4 for the above are + + scols: "lFoo|bBar|sBaz" + indices: "|uY||nX" + +Aligning properly the "|" split parts + + id col #0 col#1 col#2 + +----------+----+--------+--------+--------+ + | scols: | | "lFoo" | "bBar" | "sBaz" | + +----------+----+--------+--------+--------+ + | indices: | "" | "uY" | "" | "nX" | + +----------+----+--------+--------+--------+ + +shows that the record id() is not indexed for this table while the columns Foo +and Baz are. + +Note that there cannot be two differently named indexes for the same column and +it's intended. The indices are B+Trees[2]. The list of handles to their roots +is pointed to by hxroots with zeros for non indexed columns. For the previous +example + + tableMeta.hxroots -> {0, y, 0, x} + +where x is the root of the B+Tree for the X index and y is the root of the +B+Tree for the Y index. If there would be an index for id(), its B+Tree root +will be present where the first zero is. Similarly to hhead, hxroots is never +zero, even when there are no indices for a table. + +Table record + +A table record is an N-scalar. + + +-----+------------+--------+-------------------------------+ + | # | Name | Type | Description | + +-----+------------+--------+-------------------------------+ + | 0 | next | handle | Next record or zero. | + | 1 | id | int64 | Automatically assigned unique | + | | | | value obtainable by id(). | + | 2 | field #0 | scalar | First field of the record. | + | 3 | field #1 | scalar | Second field of the record. | + ... + | N-1 | field #N-2 | scalar | Last field of the record. | + +-----+------------+--------+-------------------------------+ + +The linked "ordering" of table records has no semantics and it doesn't have to +correlate to the order of how the records were added to the table. In fact, an +efficient way of the linking leads to "ordering" which is actually reversed wrt +the insertion order. + +Non unique index + +The composite key of the B+Tree is {indexed values, record handle}. The B+Tree +value is not used. + + B+Tree key B+Tree value + +----------------+---------------+ +--------------+ + | Indexed Values | Record Handle | -> | not used | + +----------------+---------------+ +--------------+ + +Unique index + +If the indexed values are all NULL then the composite B+Tree key is {nil, +record handle} and the B+Tree value is not used. + + B+Tree key B+Tree value + +------+-----------------+ +--------------+ + | NULL | Record Handle | -> | not used | + +------+-----------------+ +--------------+ + +If the indexed values are not all NULL then key of the B+Tree key are the indexed +values and the B+Tree value is the record handle. + + B+Tree key B+Tree value + +----------------+ +---------------+ + | Indexed Values | -> | Record Handle | + +----------------+ +---------------+ + +Non scalar types + +Scalar types of [1] are bool, complex*, float*, int*, uint*, string and []byte +types. All other types are "blob-like". + + QL type Go type + ----------------------------- + blob []byte + bigint big.Int + bigrat big.Rat + time time.Time + duration time.Duration + +Memory back-end stores the Go type directly. File back-end must resort to +encode all of the above as (tagged) []byte due to the lack of more types +supported natively by lldb. NULL values of blob-like types are encoded as nil +(gbNull in lldb/gb.go), exactly the same as the already existing QL types are. + +Blob encoding + +The values of the blob-like types are first encoded into a []byte slice: + + +-----------------------+-------------------+ + | blob | raw | + | bigint, bigrat, time | gob encoded | + | duration | gob encoded int64 | + +-----------------------+-------------------+ + +The gob encoding is "differential" wrt an initial encoding of all of the +blob-like type. IOW, the initial type descriptors which gob encoding must write +out are stripped off and "resupplied" on decoding transparently. See also +blob.go. If the length of the resulting slice is <= shortBlob, the first and +only chunk is the scalar encoding of + + + []interface{}{typeTag, slice}. // initial (and last) chunk + +The length of slice can be zero (for blob("")). If the resulting slice is long +(> shortBlob), the first chunk comes from encoding + + []interface{}{typeTag, nextHandle, firstPart}. // initial, but not final chunk + +In this case len(firstPart) <= shortBlob. Second and other chunks: If the chunk +is the last one, src is + + []interface{lastPart}. // overflow chunk (last) + +In this case len(lastPart) <= 64kB. If the chunk is not the last one, src is + + []interface{}{nextHandle, part}. // overflow chunk (not last) + +In this case len(part) == 64kB. + +Links + +Referenced from above: + + [0]: http://godoc.org/github.com/cznic/exp/lldb#hdr-Block_handles + [1]: http://godoc.org/github.com/cznic/exp/lldb#EncodeScalars + [2]: http://godoc.org/github.com/cznic/exp/lldb#BTree + +Rationale + +While these notes might be useful to anyone looking at QL sources, the +specifically intended reader is my future self. + +*/ +package design diff --git a/Godeps/_workspace/src/github.com/cznic/ql/doc.go b/Godeps/_workspace/src/github.com/cznic/ql/doc.go new file mode 100644 index 000000000..7a8baf10e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/doc.go @@ -0,0 +1,2606 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//MAYBE set operations +//MAYBE +=, -=, ... + +//TODO verify there's a graceful failure for a 2G+ blob on a 32 bit machine. + +// Package ql implements a pure Go embedded SQL database engine. +// +// QL is a member of the SQL family of languages. It is less complex and less +// powerful than SQL (whichever specification SQL is considered to be). +// +// Change list +// +// 2015-06-15: To improve compatibility with other SQL implementations, the +// count built-in aggregate function now accepts * as its argument. +// +// 2015-05-29: The execution planner was rewritten from scratch. It should use +// indices in all places where they were used before plus in some additional +// situations. It is possible to investigate the plan using the newly added +// EXPLAIN statement. The QL tool is handy for such analysis. If the planner +// would have used an index, but no such exists, the plan includes hints in +// form of copy/paste ready CREATE INDEX statements. +// +// The planner is still quite simple and a lot of work on it is yet ahead. You +// can help this process by filling an issue with a schema and query which +// fails to use an index or indices when it should, in your opinion. Bonus +// points for including output of `ql 'explain '`. +// +// 2015-05-09: The grammar of the CREATE INDEX statement now accepts an +// expression list instead of a single expression, which was further limited to +// just a column name or the built-in id(). As a side effect, composite +// indices are now functional. However, the values in the expression-list style +// index are not yet used by other statements or the statement/query planner. +// The composite index is useful while having UNIQUE clause to check for +// semantically duplicate rows before they get added to the table or when such +// a row is mutated using the UPDATE statement and the expression-list style +// index tuple of the row is thus recomputed. +// +// 2015-05-02: The Schema field of table __Table now correctly reflects any +// column constraints and/or defaults. Also, the (*DB).Info method now has that +// information provided in new ColumInfo fields NotNull, Constraint and +// Default. +// +// 2015-04-20: Added support for {LEFT,RIGHT,FULL} [OUTER] JOIN. +// +// 2015-04-18: Column definitions can now have constraints and defaults. +// Details are discussed in the "Constraints and defaults" chapter below the +// CREATE TABLE statement documentation. +// +// 2015-03-06: New built-in functions formatFloat and formatInt. Thanks +// urandom! (https://github.com/urandom) +// +// 2015-02-16: IN predicate now accepts a SELECT statement. See the updated +// "Predicates" section. +// +// 2015-01-17: Logical operators || and && have now alternative spellings: OR +// and AND (case insensitive). AND was a keyword before, but OR is a new one. +// This can possibly break existing queries. For the record, it's a good idea +// to not use any name appearing in, for example, [7] in your queries as the +// list of QL's keywords may expand for gaining better compatibility with +// existing SQL "standards". +// +// 2015-01-12: ACID guarantees were tightened at the cost of performance in +// some cases. The write collecting window mechanism, a formerly used +// implementation detail, was removed. Inserting rows one by one in a +// transaction is now slow. I mean very slow. Try to avoid inserting single +// rows in a transaction. Instead, whenever possible, perform batch updates of +// tens to, say thousands of rows in a single transaction. See also: +// http://www.sqlite.org/faq.html#q19, the discussed synchronization principles +// involved are the same as for QL, modulo minor details. +// +// Note: A side effect is that closing a DB before exiting an application, both +// for the Go API and through database/sql driver, is no more required, +// strictly speaking. Beware that exiting an application while there is an open +// (uncommitted) transaction in progress means losing the transaction data. +// However, the DB will not become corrupted because of not closing it. Nor +// that was the case before, but formerly failing to close a DB could have +// resulted in losing the data of the last transaction. +// +// 2014-09-21: id() now optionally accepts a single argument - a table name. +// +// 2014-09-01: Added the DB.Flush() method and the LIKE pattern matching +// predicate. +// +// 2014-08-08: The built in functions max and min now accept also time values. +// Thanks opennota! (https://github.com/opennota) +// +// 2014-06-05: RecordSet interface extended by new methods FirstRow and Rows. +// +// 2014-06-02: Indices on id() are now used by SELECT statements. +// +// 2014-05-07: Introduction of Marshal, Schema, Unmarshal. +// +// 2014-04-15: +// +// Added optional IF NOT EXISTS clause to CREATE INDEX and optional IF EXISTS +// clause to DROP INDEX. +// +// 2014-04-12: +// +// The column Unique in the virtual table __Index was renamed to IsUnique +// because the old name is a keyword. Unfortunately, this is a breaking change, +// sorry. +// +// 2014-04-11: Introduction of LIMIT, OFFSET. +// +// 2014-04-10: Introduction of query rewriting. +// +// 2014-04-07: Introduction of indices. +// +// Building non CGO QL +// +// QL imports zappy[8], a block-based compressor, which speeds up its +// performance by using a C version of the compression/decompression +// algorithms. If a CGO-free (pure Go) version of QL, or an app using QL, is +// required, please include 'purego' in the -tags option of go +// {build,get,install}. For example: +// +// $ go get -tags purego github.com/cznic/ql +// +// If zappy was installed before installing QL, it might be necessary to +// rebuild zappy first (or rebuild QL with all its dependencies using the -a +// option): +// +// $ touch "$GOPATH"/src/github.com/cznic/zappy/*.go +// $ go install -tags purego github.com/cznic/zappy +// $ go install github.com/cznic/ql +// +// Notation +// +// The syntax is specified using Extended Backus-Naur Form (EBNF) +// +// Production = production_name "=" [ Expression ] "." . +// Expression = Alternative { "|" Alternative } . +// Alternative = Term { Term } . +// Term = production_name | token [ "…" token ] | Group | Option | Repetition . +// Group = "(" Expression ")" . +// Option = "[" Expression "]" . +// Repetition = "{" Expression "}" . +// Productions are expressions constructed from terms and the following operators, in increasing precedence +// +// | alternation +// () grouping +// [] option (0 or 1 times) +// {} repetition (0 to n times) +// +// Lower-case production names are used to identify lexical tokens. +// Non-terminals are in CamelCase. Lexical tokens are enclosed in double quotes +// "" or back quotes ``. +// +// The form a … b represents the set of characters from a through b as +// alternatives. The horizontal ellipsis … is also used elsewhere in the spec +// to informally denote various enumerations or code snippets that are not +// further specified. +// +// QL source code representation +// +// QL source code is Unicode text encoded in UTF-8. The text is not +// canonicalized, so a single accented code point is distinct from the same +// character constructed from combining an accent and a letter; those are +// treated as two code points. For simplicity, this document will use the +// unqualified term character to refer to a Unicode code point in the source +// text. +// +// Each code point is distinct; for instance, upper and lower case letters are +// different characters. +// +// Implementation restriction: For compatibility with other tools, the parser +// may disallow the NUL character (U+0000) in the statement. +// +// Implementation restriction: A byte order mark is disallowed anywhere in QL +// statements. +// +// Characters +// +// The following terms are used to denote specific character classes +// +// newline = . // the Unicode code point U+000A +// unicode_char = . // an arbitrary Unicode code point except newline +// ascii_letter = "a" … "z" | "A" … "Z" . +// +// Letters and digits +// +// The underscore character _ (U+005F) is considered a letter. +// +// letter = ascii_letter | "_" . +// decimal_digit = "0" … "9" . +// octal_digit = "0" … "7" . +// hex_digit = "0" … "9" | "A" … "F" | "a" … "f" . +// +// Lexical elements +// +// Lexical elements are comments, tokens, identifiers, keywords, operators and +// delimiters, integer, floating-point, imaginary, rune and string literals and +// QL parameters. +// +// Comments +// +// There are three forms of comments +// +// Line comments start with the character sequence // or -- and stop at the end +// of the line. A line comment acts like a space. +// +// General comments start with the character sequence /* and continue through +// the character sequence */. A general comment acts like a space. +// +// Comments do not nest. +// +// Tokens +// +// Tokens form the vocabulary of QL. There are four classes: identifiers, +// keywords, operators and delimiters, and literals. White space, formed from +// spaces (U+0020), horizontal tabs (U+0009), carriage returns (U+000D), and +// newlines (U+000A), is ignored except as it separates tokens that would +// otherwise combine into a single token. +// +// Semicolons +// +// The formal grammar uses semicolons ";" as separators of QL statements. A +// single QL statement or the last QL statement in a list of statements can +// have an optional semicolon terminator. (Actually a separator from the +// following empty statement.) +// +// Identifiers +// +// Identifiers name entities such as tables or record set columns. An +// identifier is a sequence of one or more letters and digits. The first +// character in an identifier must be a letter. +// +// identifier = letter { letter | decimal_digit } . +// +// For example +// +// price +// _tmp42 +// Sales +// +// No identifiers are predeclared, however note that no keyword can be used as +// an identifier. Identifiers starting with two underscores are used for meta +// data virtual tables names. For forward compatibility, users should generally +// avoid using any identifiers starting with two underscores. For example +// +// __Column +// __Column2 +// __Index +// __Table +// +// Keywords +// +// The following keywords are reserved and may not be used as identifiers. +// +// ADD COLUMN false int32 ORDER uint16 +// ALTER complex128 float int64 OUTER uint32 +// AND complex64 float32 int8 RIGHT uint64 +// AS CREATE float64 INTO SELECT uint8 +// ASC DEFAULT FROM JOIN SET UNIQUE +// BETWEEN DELETE GROUP LEFT string UPDATE +// bigint DESC IF LIMIT TABLE VALUES +// bigrat DISTINCT IN LIKE time WHERE +// blob DROP INDEX NOT true +// bool duration INSERT NULL OR +// BY EXISTS int OFFSET TRUNCATE +// byte EXPLAIN int16 ON uint +// +// Keywords are not case sensitive. +// +// Operators and Delimiters +// +// The following character sequences represent operators, delimiters, and other +// special tokens +// +// + & && == != ( ) +// - | || < <= [ ] +// * ^ > >= , ; +// / << = . +// % >> ! +// &^ +// +// Operators consisting of more than one character are referred to by names in +// the rest of the documentation +// +// andand = "&&" . +// andnot = "&^" . +// lsh = "<<" . +// le = "<=" . +// eq = "==" . +// ge = ">=" . +// neq = "!=" . +// oror = "||" . +// rsh = ">>" . +// +// Integer literals +// +// An integer literal is a sequence of digits representing an integer constant. +// An optional prefix sets a non-decimal base: 0 for octal, 0x or 0X for +// hexadecimal. In hexadecimal literals, letters a-f and A-F represent values +// 10 through 15. +// +// int_lit = decimal_lit | octal_lit | hex_lit . +// decimal_lit = ( "1" … "9" ) { decimal_digit } . +// octal_lit = "0" { octal_digit } . +// hex_lit = "0" ( "x" | "X" ) hex_digit { hex_digit } . +// +// For example +// +// 42 +// 0600 +// 0xBadFace +// 1701411834604692 +// +// Floating-point literals +// +// A floating-point literal is a decimal representation of a floating-point +// constant. It has an integer part, a decimal point, a fractional part, and an +// exponent part. The integer and fractional part comprise decimal digits; the +// exponent part is an e or E followed by an optionally signed decimal +// exponent. One of the integer part or the fractional part may be elided; one +// of the decimal point or the exponent may be elided. +// +// float_lit = decimals "." [ decimals ] [ exponent ] | +// decimals exponent | +// "." decimals [ exponent ] . +// decimals = decimal_digit { decimal_digit } . +// exponent = ( "e" | "E" ) [ "+" | "-" ] decimals . +// +// For example +// +// 0. +// 72.40 +// 072.40 // == 72.40 +// 2.71828 +// 1.e+0 +// 6.67428e-11 +// 1E6 +// .25 +// .12345E+5 +// +// Imaginary literals +// +// An imaginary literal is a decimal representation of the imaginary part of a +// complex constant. It consists of a floating-point literal or decimal integer +// followed by the lower-case letter i. +// +// imaginary_lit = (decimals | float_lit) "i" . +// +// For example +// +// 0i +// 011i // == 11i +// 0.i +// 2.71828i +// 1.e+0i +// 6.67428e-11i +// 1E6i +// .25i +// .12345E+5i +// +// Rune literals +// +// A rune literal represents a rune constant, an integer value identifying a +// Unicode code point. A rune literal is expressed as one or more characters +// enclosed in single quotes. Within the quotes, any character may appear +// except single quote and newline. A single quoted character represents the +// Unicode value of the character itself, while multi-character sequences +// beginning with a backslash encode values in various formats. +// +// The simplest form represents the single character within the quotes; since +// QL statements are Unicode characters encoded in UTF-8, multiple +// UTF-8-encoded bytes may represent a single integer value. For instance, the +// literal 'a' holds a single byte representing a literal a, Unicode U+0061, +// value 0x61, while 'ä' holds two bytes (0xc3 0xa4) representing a literal +// a-dieresis, U+00E4, value 0xe4. +// +// Several backslash escapes allow arbitrary values to be encoded as ASCII +// text. There are four ways to represent the integer value as a numeric +// constant: \x followed by exactly two hexadecimal digits; \u followed by +// exactly four hexadecimal digits; \U followed by exactly eight hexadecimal +// digits, and a plain backslash \ followed by exactly three octal digits. In +// each case the value of the literal is the value represented by the digits in +// the corresponding base. +// +// Although these representations all result in an integer, they have different +// valid ranges. Octal escapes must represent a value between 0 and 255 +// inclusive. Hexadecimal escapes satisfy this condition by construction. The +// escapes \u and \U represent Unicode code points so within them some values +// are illegal, in particular those above 0x10FFFF and surrogate halves. +// +// After a backslash, certain single-character escapes represent special +// values +// +// \a U+0007 alert or bell +// \b U+0008 backspace +// \f U+000C form feed +// \n U+000A line feed or newline +// \r U+000D carriage return +// \t U+0009 horizontal tab +// \v U+000b vertical tab +// \\ U+005c backslash +// \' U+0027 single quote (valid escape only within rune literals) +// \" U+0022 double quote (valid escape only within string literals) +// +// All other sequences starting with a backslash are illegal inside rune +// literals. +// +// rune_lit = "'" ( unicode_value | byte_value ) "'" . +// unicode_value = unicode_char | little_u_value | big_u_value | escaped_char . +// byte_value = octal_byte_value | hex_byte_value . +// octal_byte_value = `\` octal_digit octal_digit octal_digit . +// hex_byte_value = `\` "x" hex_digit hex_digit . +// little_u_value = `\` "u" hex_digit hex_digit hex_digit hex_digit . +// big_u_value = `\` "U" hex_digit hex_digit hex_digit hex_digit +// hex_digit hex_digit hex_digit hex_digit . +// escaped_char = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) . +// +// For example +// +// 'a' +// 'ä' +// '本' +// '\t' +// '\000' +// '\007' +// '\377' +// '\x07' +// '\xff' +// '\u12e4' +// '\U00101234' +// 'aa' // illegal: too many characters +// '\xa' // illegal: too few hexadecimal digits +// '\0' // illegal: too few octal digits +// '\uDFFF' // illegal: surrogate half +// '\U00110000' // illegal: invalid Unicode code point +// +// String literals +// +// A string literal represents a string constant obtained from concatenating a +// sequence of characters. There are two forms: raw string literals and +// interpreted string literals. +// +// Raw string literals are character sequences between back quotes ``. Within +// the quotes, any character is legal except back quote. The value of a raw +// string literal is the string composed of the uninterpreted (implicitly +// UTF-8-encoded) characters between the quotes; in particular, backslashes +// have no special meaning and the string may contain newlines. Carriage +// returns inside raw string literals are discarded from the raw string value. +// +// Interpreted string literals are character sequences between double quotes +// "". The text between the quotes, which may not contain newlines, forms the +// value of the literal, with backslash escapes interpreted as they are in rune +// literals (except that \' is illegal and \" is legal), with the same +// restrictions. The three-digit octal (\nnn) and two-digit hexadecimal (\xnn) +// escapes represent individual bytes of the resulting string; all other +// escapes represent the (possibly multi-byte) UTF-8 encoding of individual +// characters. Thus inside a string literal \377 and \xFF represent a single +// byte of value 0xFF=255, while ÿ, \u00FF, \U000000FF and \xc3\xbf represent +// the two bytes 0xc3 0xbf of the UTF-8 encoding of character U+00FF. +// +// string_lit = raw_string_lit | interpreted_string_lit . +// raw_string_lit = "`" { unicode_char | newline } "`" . +// interpreted_string_lit = `"` { unicode_value | byte_value } `"` . +// +// For example +// +// `abc` // same as "abc" +// `\n +// \n` // same as "\\n\n\\n" +// "\n" +// "" +// "Hello, world!\n" +// "日本語" +// "\u65e5本\U00008a9e" +// "\xff\u00FF" +// "\uD800" // illegal: surrogate half +// "\U00110000" // illegal: invalid Unicode code point +// +// These examples all represent the same string +// +// "日本語" // UTF-8 input text +// `日本語` // UTF-8 input text as a raw literal +// "\u65e5\u672c\u8a9e" // the explicit Unicode code points +// "\U000065e5\U0000672c\U00008a9e" // the explicit Unicode code points +// "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" // the explicit UTF-8 bytes +// +// If the statement source represents a character as two code points, such as a +// combining form involving an accent and a letter, the result will be an error +// if placed in a rune literal (it is not a single code point), and will appear +// as two code points if placed in a string literal. +// +// QL parameters +// +// Literals are assigned their values from the respective text representation +// at "compile" (parse) time. QL parameters provide the same functionality as +// literals, but their value is assigned at execution time from an expression +// list passed to DB.Run or DB.Execute. Using '?' or '$' is completely +// equivalent. +// +// ql_parameter = ( "?" | "$" ) "1" … "9" { "0" … "9" } . +// +// For example +// +// SELECT DepartmentID +// FROM department +// WHERE DepartmentID == ?1 +// ORDER BY DepartmentName; +// +// SELECT employee.LastName +// FROM department, employee +// WHERE department.DepartmentID == $1 && employee.LastName > $2 +// ORDER BY DepartmentID; +// +// Constants +// +// Keywords 'false' and 'true' (not case sensitive) represent the two possible +// constant values of type bool (also not case sensitive). +// +// Keyword 'NULL' (not case sensitive) represents an untyped constant which is +// assignable to any type. NULL is distinct from any other value of any type. +// +// Types +// +// A type determines the set of values and operations specific to values of +// that type. A type is specified by a type name. +// +// Type = "bigint" // http://golang.org/pkg/math/big/#Int +// | "bigrat" // http://golang.org/pkg/math/big/#Rat +// | "blob" // []byte +// | "bool" +// | "byte" // alias for uint8 +// | "complex128" +// | "complex64" +// | "duration" // http://golang.org/pkg/time/#Duration +// | "float" // alias for float64 +// | "float32" +// | "float64" +// | "int" // alias for int64 +// | "int16" +// | "int32" +// | "int64" +// | "int8" +// | "rune" // alias for int32 +// | "string" +// | "time" // http://golang.org/pkg/time/#Time +// | "uint" // alias for uint64 +// | "uint16" +// | "uint32" +// | "uint64" +// | "uint8" . +// +// Named instances of the boolean, numeric, and string types are keywords. The +// names are not case sensitive. +// +// Note: The blob type is exchanged between the back end and the API as []byte. +// On 32 bit platforms this limits the size which the implementation can handle +// to 2G. +// +// Boolean types +// +// A boolean type represents the set of Boolean truth values denoted by the +// predeclared constants true and false. The predeclared boolean type is bool. +// +// Duration type +// +// A duration type represents the elapsed time between two instants as an int64 +// nanosecond count. The representation limits the largest representable +// duration to approximately 290 years. +// +// Numeric types +// +// A numeric type represents sets of integer or floating-point values. The +// predeclared architecture-independent numeric types are +// +// uint8 the set of all unsigned 8-bit integers (0 to 255) +// uint16 the set of all unsigned 16-bit integers (0 to 65535) +// uint32 the set of all unsigned 32-bit integers (0 to 4294967295) +// uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615) +// +// int8 the set of all signed 8-bit integers (-128 to 127) +// int16 the set of all signed 16-bit integers (-32768 to 32767) +// int32 the set of all signed 32-bit integers (-2147483648 to 2147483647) +// int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807) +// duration the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807) +// bigint the set of all integers +// +// bigrat the set of all rational numbers +// +// float32 the set of all IEEE-754 32-bit floating-point numbers +// float64 the set of all IEEE-754 64-bit floating-point numbers +// +// complex64 the set of all complex numbers with float32 real and imaginary parts +// complex128 the set of all complex numbers with float64 real and imaginary parts +// +// byte alias for uint8 +// float alias for float64 +// int alias for int64 +// rune alias for int32 +// uint alias for uint64 +// +// The value of an n-bit integer is n bits wide and represented using two's +// complement arithmetic. +// +// Conversions are required when different numeric types are mixed in an +// expression or assignment. +// +// String types +// +// A string type represents the set of string values. A string value is a +// (possibly empty) sequence of bytes. The case insensitive keyword for the +// string type is 'string'. +// +// The length of a string (its size in bytes) can be discovered using the +// built-in function len. +// +// Time types +// +// A time type represents an instant in time with nanosecond precision. Each +// time has associated with it a location, consulted when computing the +// presentation form of the time. +// +// Predeclared functions +// +// The following functions are implicitly declared +// +// avg complex contains count date +// day formatTime formatFloat formatInt +// hasPrefix hasSuffix hour hours id +// imag len max min minute +// minutes month nanosecond nanoseconds now +// parseTime real second seconds since +// sum timeIn weekday year yearDay +// +// Expressions +// +// An expression specifies the computation of a value by applying operators and +// functions to operands. +// +// Operands +// +// Operands denote the elementary values in an expression. An operand may be a +// literal, a (possibly qualified) identifier denoting a constant or a function +// or a table/record set column, or a parenthesized expression. +// +// Operand = Literal | QualifiedIdent | "(" Expression ")" . +// Literal = "FALSE" | "NULL" | "TRUE" +// | float_lit | imaginary_lit | int_lit | rune_lit | string_lit +// | ql_parameter . +// +// Qualified identifiers +// +// A qualified identifier is an identifier qualified with a table/record set +// name prefix. +// +// QualifiedIdent = identifier [ "." identifier ] . +// +// For example +// +// invoice.Num // might denote column 'Num' from table 'invoice' +// +// Primary expressions +// +// Primary expression are the operands for unary and binary expressions. +// +// PrimaryExpression = Operand +// | Conversion +// | PrimaryExpression Index +// | PrimaryExpression Slice +// | PrimaryExpression Call . +// +// Call = "(" [ "*" | ExpressionList ] ")" . // * only in count(*). +// Index = "[" Expression "]" . +// Slice = "[" [ Expression ] ":" [ Expression ] "]" . +// +// For example +// +// x +// 2 +// (s + ".txt") +// f(3.1415, true) +// s[i : j + 1] +// +// Index expressions +// +// A primary expression of the form +// +// s[x] +// +// denotes the element of a string indexed by x. Its type is byte. The value x +// is called the index. The following rules apply +// +// - The index x must be of integer type except bigint or duration; it is in +// range if 0 <= x < len(s), otherwise it is out of range. +// +// - A constant index must be non-negative and representable by a value of type +// int. +// +// - A constant index must be in range if the string a is a literal. +// +// - If x is out of range at run time, a run-time error occurs. +// +// - s[x] is the byte at index x and the type of s[x] is byte. +// +// If s is NULL or x is NULL then the result is NULL. +// +// Otherwise s[x] is illegal. +// +// Slices +// +// For a string, the primary expression +// +// s[low : high] +// +// constructs a substring. The indices low and high select which elements +// appear in the result. The result has indices starting at 0 and length equal +// to high - low. +// +// For convenience, any of the indices may be omitted. A missing low index +// defaults to zero; a missing high index defaults to the length of the sliced +// operand +// +// s[2:] // same s[2 : len(s)] +// s[:3] // same as s[0 : 3] +// s[:] // same as s[0 : len(s)] +// +// The indices low and high are in range if 0 <= low <= high <= len(a), +// otherwise they are out of range. A constant index must be non-negative and +// representable by a value of type int. If both indices are constant, they +// must satisfy low <= high. If the indices are out of range at run time, a +// run-time error occurs. +// +// Integer values of type bigint or duration cannot be used as indices. +// +// If s is NULL the result is NULL. If low or high is not omitted and is NULL +// then the result is NULL. +// +// Calls +// +// Given an identifier f denoting a predeclared function, +// +// f(a1, a2, … an) +// +// calls f with arguments a1, a2, … an. Arguments are evaluated before the +// function is called. The type of the expression is the result type of f. +// +// complex(x, y) +// len(name) +// +// In a function call, the function value and arguments are evaluated in the +// usual order. After they are evaluated, the parameters of the call are passed +// by value to the function and the called function begins execution. The +// return value of the function is passed by value when the function returns. +// +// Calling an undefined function causes a compile-time error. +// +// Operators +// +// Operators combine operands into expressions. +// +// Expression = Term { ( oror | "OR" ) Term } . +// +// ExpressionList = Expression { "," Expression } [ "," ]. +// Factor = PrimaryFactor { ( ge | ">" | le | "<" | neq | eq | "LIKE" ) PrimaryFactor } [ Predicate ] . +// PrimaryFactor = PrimaryTerm { ( "^" | "|" | "-" | "+" ) PrimaryTerm } . +// PrimaryTerm = UnaryExpr { ( andnot | "&" | lsh | rsh | "%" | "/" | "*" ) UnaryExpr } . +// Term = Factor { ( andand | "AND" ) Factor } . +// UnaryExpr = [ "^" | "!" | "-" | "+" ] PrimaryExpression . +// +// Comparisons are discussed elsewhere. For other binary operators, the operand +// types must be identical unless the operation involves shifts or untyped +// constants. For operations involving constants only, see the section on +// constant expressions. +// +// Except for shift operations, if one operand is an untyped constant and the +// other operand is not, the constant is converted to the type of the other +// operand. +// +// The right operand in a shift expression must have unsigned integer type or +// be an untyped constant that can be converted to unsigned integer type. If +// the left operand of a non-constant shift expression is an untyped constant, +// the type of the constant is what it would be if the shift expression were +// replaced by its left operand alone. +// +// Pattern matching +// +// Expressions of the form +// +// expr1 LIKE expr2 +// +// yeild a boolean value true if expr2, a regular expression, matches expr1 +// (see also [6]). Both expression must be of type string. If any one of the +// expressions is NULL the result is NULL. +// +// Predicates +// +// Predicates are special form expressions having a boolean result type. +// +// Expressions of the form +// +// expr IN ( expr1, expr2, expr3, ... ) // case A +// +// expr NOT IN ( expr1, expr2, expr3, ... ) // case B +// +// are equivalent, including NULL handling, to +// +// expr == expr1 || expr == expr2 || expr == expr3 || ... // case A +// +// expr != expr1 && expr != expr2 && expr != expr3 && ... // case B +// +// The types of involved expressions must be comparable as defined in +// "Comparison operators". +// +// Another form of the IN predicate creates the expression list from a result +// of a SelectStmt. +// +// DELETE FROM t WHERE id() IN (SELECT id_t FROM u WHERE inactive_days > 365) +// +// The SelectStmt must select only one column. The produced expression list is +// resource limited by the memory available to the process. NULL values +// produced by the SelectStmt are ignored, but if all records of the SelectStmt +// are NULL the predicate yields NULL. The select statement is evaluated only +// once. If the type of expr is not the same as the type of the field returned +// by the SelectStmt then the set operation yields false. The type of the +// column returned by the SelectStmt must be one of the simple (non blob-like) +// types: +// +// bool +// byte // alias uint8 +// complex128 +// complex64 +// float // alias float64 +// float32 +// float64 +// int // alias int64 +// int16 +// int32 +// int64 +// int8 +// rune // alias int32 +// string +// uint // alias uint64 +// uint16 +// uint32 +// uint64 +// uint8 +// +// Expressions of the form +// +// expr BETWEEN low AND high // case A +// +// expr NOT BETWEEN low AND high // case B +// +// are equivalent, including NULL handling, to +// +// expr >= low && expr <= high // case A +// +// expr < low || expr > high // case B +// +// The types of involved expressions must be ordered as defined in "Comparison +// operators". +// +// Predicate = ( +// [ "NOT" ] ( +// "IN" "(" ExpressionList ")" +// | "IN" "(" SelectStmt [ ";" ] ")" +// | "BETWEEN" PrimaryFactor "AND" PrimaryFactor +// ) +// | "IS" [ "NOT" ] "NULL" +// ). +// +// Expressions of the form +// +// expr IS NULL // case A +// +// expr IS NOT NULL // case B +// +// yeild a boolean value true if expr does not have a specific type (case A) or +// if expr has a specific type (case B). In other cases the result is a boolean +// value false. +// +// Operator precedence +// +// Unary operators have the highest precedence. +// +// There are five precedence levels for binary operators. Multiplication +// operators bind strongest, followed by addition operators, comparison +// operators, && (logical AND), and finally || (logical OR) +// +// Precedence Operator +// 5 * / % << >> & &^ +// 4 + - | ^ +// 3 == != < <= > >= +// 2 && +// 1 || +// +// Binary operators of the same precedence associate from left to right. For +// instance, x / y * z is the same as (x / y) * z. +// +// +x +// 23 + 3*x[i] +// x <= f() +// ^a >> b +// f() || g() +// x == y+1 && z > 0 +// +// Note that the operator precedence is reflected explicitly by the grammar. +// +// Arithmetic operators +// +// Arithmetic operators apply to numeric values and yield a result of the same +// type as the first operand. The four standard arithmetic operators (+, -, *, +// /) apply to integer, rational, floating-point, and complex types; + also +// applies to strings; +,- also applies to times. All other arithmetic +// operators apply to integers only. +// +// + sum integers, rationals, floats, complex values, strings +// - difference integers, rationals, floats, complex values, times +// * product integers, rationals, floats, complex values +// / quotient integers, rationals, floats, complex values +// % remainder integers +// +// & bitwise AND integers +// | bitwise OR integers +// ^ bitwise XOR integers +// &^ bit clear (AND NOT) integers +// +// << left shift integer << unsigned integer +// >> right shift integer >> unsigned integer +// +// Strings can be concatenated using the + operator +// +// "hi" + string(c) + " and good bye" +// +// String addition creates a new string by concatenating the operands. +// +// A value of type duration can be added to or subtracted from a value of type time. +// +// now() + duration("1h") // time after 1 hour from now +// duration("1h") + now() // time after 1 hour from now +// now() - duration("1h") // time before 1 hour from now +// duration("1h") - now() // illegal, negative times do not exist +// +// Times can subtracted from each other producing a value of type duration. +// +// now() - t0 // elapsed time since t0 +// now() + now() // illegal, operator + not defined for times +// +// For two integer values x and y, the integer quotient q = x / y and remainder +// r = x % y satisfy the following relationships +// +// x = q*y + r and |r| < |y| +// +// with x / y truncated towards zero ("truncated division"). +// +// x y x / y x % y +// 5 3 1 2 +// -5 3 -1 -2 +// 5 -3 -1 2 +// -5 -3 1 -2 +// +// As an exception to this rule, if the dividend x is the most negative value +// for the int type of x, the quotient q = x / -1 is equal to x (and r = 0). +// +// x, q +// int8 -128 +// int16 -32768 +// int32 -2147483648 +// int64 -9223372036854775808 +// +// If the divisor is a constant expression, it must not be zero. If the divisor +// is zero at run time, a run-time error occurs. If the dividend is +// non-negative and the divisor is a constant power of 2, the division may be +// replaced by a right shift, and computing the remainder may be replaced by a +// bitwise AND operation +// +// x x / 4 x % 4 x >> 2 x & 3 +// 11 2 3 2 3 +// -11 -2 -3 -3 1 +// +// The shift operators shift the left operand by the shift count specified by +// the right operand. They implement arithmetic shifts if the left operand is a +// signed integer and logical shifts if it is an unsigned integer. There is no +// upper limit on the shift count. Shifts behave as if the left operand is +// shifted n times by 1 for a shift count of n. As a result, x << 1 is the same +// as x*2 and x >> 1 is the same as x/2 but truncated towards negative +// infinity. +// +// For integer operands, the unary operators +, -, and ^ are defined as follows +// +// +x is 0 + x +// -x negation is 0 - x +// ^x bitwise complement is m ^ x with m = "all bits set to 1" for unsigned x +// and m = -1 for signed x +// +// For floating-point and complex numbers, +x is the same as x, while -x is the +// negation of x. The result of a floating-point or complex division by zero is +// not specified beyond the IEEE-754 standard; whether a run-time error occurs +// is implementation-specific. +// +// Whenever any operand of any arithmetic operation, unary or binary, is NULL, +// as well as in the case of the string concatenating operation, the result is +// NULL. +// +// 42*NULL // the result is NULL +// NULL/x // the result is NULL +// "foo"+NULL // the result is NULL +// +// Integer overflow +// +// For unsigned integer values, the operations +, -, *, and << are computed +// modulo 2n, where n is the bit width of the unsigned integer's type. Loosely +// speaking, these unsigned integer operations discard high bits upon overflow, +// and expressions may rely on ``wrap around''. +// +// For signed integers with a finite bit width, the operations +, -, *, and << +// may legally overflow and the resulting value exists and is deterministically +// defined by the signed integer representation, the operation, and its +// operands. No exception is raised as a result of overflow. An evaluator may +// not optimize an expression under the assumption that overflow does not +// occur. For instance, it may not assume that x < x + 1 is always true. +// +// Integers of type bigint and rationals do not overflow but their handling is +// limited by the memory resources available to the program. +// +// Comparison operators +// +// Comparison operators compare two operands and yield a boolean value. +// +// == equal +// != not equal +// < less +// <= less or equal +// > greater +// >= greater or equal +// +// In any comparison, the first operand must be of same type as is the second +// operand, or vice versa. +// +// The equality operators == and != apply to operands that are comparable. The +// ordering operators <, <=, >, and >= apply to operands that are ordered. +// These terms and the result of the comparisons are defined as follows +// +// - Boolean values are comparable. Two boolean values are equal if they are +// either both true or both false. +// +// - Complex values are comparable. Two complex values u and v are equal if +// both real(u) == real(v) and imag(u) == imag(v). +// +// - Integer values are comparable and ordered, in the usual way. Note that +// durations are integers. +// +// - Floating point values are comparable and ordered, as defined by the +// IEEE-754 standard. +// +// - Rational values are comparable and ordered, in the usual way. +// +// - String values are comparable and ordered, lexically byte-wise. +// +// - Time values are comparable and ordered. +// +// Whenever any operand of any comparison operation is NULL, the result is +// NULL. +// +// Note that slices are always of type string. +// +// Logical operators +// +// Logical operators apply to boolean values and yield a boolean result. The +// right operand is evaluated conditionally. +// +// && conditional AND p && q is "if p then q else false" +// || conditional OR p || q is "if p then true else q" +// ! NOT !p is "not p" +// +// The truth tables for logical operations with NULL values +// +// +-------+-------+---------+---------+ +// | p | q | p || q | p && q | +// +-------+-------+---------+---------+ +// | true | true | *true | true | +// | true | false | *true | false | +// | true | NULL | *true | NULL | +// | false | true | true | *false | +// | false | false | false | *false | +// | false | NULL | NULL | *false | +// | NULL | true | true | NULL | +// | NULL | false | NULL | false | +// | NULL | NULL | NULL | NULL | +// +-------+-------+---------+---------+ +// * indicates q is not evaluated. +// +// +-------+-------+ +// | p | !p | +// +-------+-------+ +// | true | false | +// | false | true | +// | NULL | NULL | +// +-------+-------+ +// +// Conversions +// +// Conversions are expressions of the form T(x) where T is a type and x is an +// expression that can be converted to type T. +// +// Conversion = Type "(" Expression ")" . +// +// A constant value x can be converted to type T in any of these cases: +// +// - x is representable by a value of type T. +// +// - x is a floating-point constant, T is a floating-point type, and x is +// representable by a value of type T after rounding using IEEE 754 +// round-to-even rules. The constant T(x) is the rounded value. +// +// - x is an integer constant and T is a string type. The same rule as for +// non-constant x applies in this case. +// +// Converting a constant yields a typed constant as result. +// +// float32(2.718281828) // 2.718281828 of type float32 +// complex128(1) // 1.0 + 0.0i of type complex128 +// float32(0.49999999) // 0.5 of type float32 +// string('x') // "x" of type string +// string(0x266c) // "♬" of type string +// "foo" + "bar" // "foobar" +// int(1.2) // illegal: 1.2 cannot be represented as an int +// string(65.0) // illegal: 65.0 is not an integer constant +// +// A non-constant value x can be converted to type T in any of these cases: +// +// - x has type T. +// +// - x's type and T are both integer or floating point types. +// +// - x's type and T are both complex types. +// +// - x is an integer, except bigint or duration, and T is a string type. +// +// Specific rules apply to (non-constant) conversions between numeric types or +// to and from a string type. These conversions may change the representation +// of x and incur a run-time cost. All other conversions only change the type +// but not the representation of x. +// +// A conversion of NULL to any type yields NULL. +// +// Conversions between numeric types +// +// For the conversion of non-constant numeric values, the following rules +// apply +// +// 1. When converting between integer types, if the value is a signed integer, +// it is sign extended to implicit infinite precision; otherwise it is zero +// extended. It is then truncated to fit in the result type's size. For +// example, if v == uint16(0x10F0), then uint32(int8(v)) == 0xFFFFFFF0. The +// conversion always yields a valid value; there is no indication of overflow. +// +// 2. When converting a floating-point number to an integer, the fraction is +// discarded (truncation towards zero). +// +// 3. When converting an integer or floating-point number to a floating-point +// type, or a complex number to another complex type, the result value is +// rounded to the precision specified by the destination type. For instance, +// the value of a variable x of type float32 may be stored using additional +// precision beyond that of an IEEE-754 32-bit number, but float32(x) +// represents the result of rounding x's value to 32-bit precision. Similarly, +// x + 0.1 may use more than 32 bits of precision, but float32(x + 0.1) does +// not. +// +// In all non-constant conversions involving floating-point or complex values, +// if the result type cannot represent the value the conversion succeeds but +// the result value is implementation-dependent. +// +// Conversions to and from a string type +// +// 1. Converting a signed or unsigned integer value to a string type yields a +// string containing the UTF-8 representation of the integer. Values outside +// the range of valid Unicode code points are converted to "\uFFFD". +// +// string('a') // "a" +// string(-1) // "\ufffd" == "\xef\xbf\xbd" +// string(0xf8) // "\u00f8" == "ø" == "\xc3\xb8" +// string(0x65e5) // "\u65e5" == "日" == "\xe6\x97\xa5" +// +// 2. Converting a blob to a string type yields a string whose successive bytes +// are the elements of the blob. +// +// string(b /* []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} */) // "hellø" +// string(b /* []byte{} */) // "" +// string(b /* []byte(nil) */) // "" +// +// 3. Converting a value of a string type to a blob yields a blob whose +// successive elements are the bytes of the string. +// +// blob("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} +// blob("") // []byte{} +// +// 4. Converting a value of a bigint type to a string yields a string +// containing the decimal decimal representation of the integer. +// +// string(M9) // "2305843009213693951" +// +// 5. Converting a value of a string type to a bigint yields a bigint value +// containing the integer represented by the string value. A prefix of “0x” or +// “0X” selects base 16; the “0” prefix selects base 8, and a “0b” or “0B” +// prefix selects base 2. Otherwise the value is interpreted in base 10. An +// error occurs if the string value is not in any valid format. +// +// bigint("2305843009213693951") // M9 +// bigint("0x1ffffffffffffffffffffff") // M10 == 2^89-1 +// +// 6. Converting a value of a rational type to a string yields a string +// containing the decimal decimal representation of the rational in the form +// "a/b" (even if b == 1). +// +// string(bigrat(355)/bigrat(113)) // "355/113" +// +// 7. Converting a value of a string type to a bigrat yields a bigrat value +// containing the rational represented by the string value. The string can be +// given as a fraction "a/b" or as a floating-point number optionally followed +// by an exponent. An error occurs if the string value is not in any valid +// format. +// +// bigrat("1.2e-34") +// bigrat("355/113") +// +// 8. Converting a value of a duration type to a string returns a string +// representing the duration in the form "72h3m0.5s". Leading zero units are +// omitted. As a special case, durations less than one second format using a +// smaller unit (milli-, micro-, or nanoseconds) to ensure that the leading +// digit is non-zero. The zero duration formats as 0, with no unit. +// +// string(elapsed) // "1h", for example +// +// 9. Converting a string value to a duration yields a duration represented by +// the string. A duration string is a possibly signed sequence of decimal +// numbers, each with optional fraction and a unit suffix, such as "300ms", +// "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", +// "m", "h". +// +// duration("1m") // http://golang.org/pkg/time/#Minute +// +// 10. Converting a time value to a string returns the time formatted using the +// format string +// +// "2006-01-02 15:04:05.999999999 -0700 MST" +// +// Order of evaluation +// +// When evaluating the operands of an expression or of function calls, +// operations are evaluated in lexical left-to-right order. +// +// For example, in the evaluation of +// +// g(h(), i()+x[j()], c) +// +// the function calls and evaluation of c happen in the order h(), i(), j(), c. +// +// Floating-point operations within a single expression are evaluated according +// to the associativity of the operators. Explicit parentheses affect the +// evaluation by overriding the default associativity. In the expression x + (y +// + z) the addition y + z is performed before adding x. +// +// Statements +// +// Statements control execution. +// +// Statement = EmptyStmt | AlterTableStmt | BeginTransactionStmt | CommitStmt +// | CreateIndexStmt | CreateTableStmt | DeleteFromStmt | DropIndexStmt +// | DropTableStmt | InsertIntoStmt | RollbackStmt | SelectStmt +// | TruncateTableStmt | UpdateStmt | ExplainStmt. +// +// StatementList = Statement { ";" Statement } . +// +// Empty statements +// +// The empty statement does nothing. +// +// EmptyStmt = . +// +// ALTER TABLE +// +// Alter table statements modify existing tables. With the ADD clause it adds +// a new column to the table. The column must not exist. With the DROP clause +// it removes an existing column from a table. The column must exist and it +// must be not the only (last) column of the table. IOW, there cannot be a +// table with no columns. +// +// AlterTableStmt = "ALTER" "TABLE" TableName ( "ADD" ColumnDef | "DROP" "COLUMN" ColumnName ) . +// +// For example +// +// BEGIN TRANSACTION; +// ALTER TABLE Stock ADD Qty int; +// ALTER TABLE Income DROP COLUMN Taxes; +// COMMIT; +// +// When adding a column to a table with existing data, the constraint clause of +// the ColumnDef cannot be used. Adding a constrained column to an empty table +// is fine. +// +// BEGIN TRANSACTION +// +// Begin transactions statements introduce a new transaction level. Every +// transaction level must be eventually balanced by exactly one of COMMIT or +// ROLLBACK statements. Note that when a transaction is roll-backed because of +// a statement failure then no explicit balancing of the respective BEGIN +// TRANSACTION is statement is required nor permitted. +// +// Failure to properly balance any opened transaction level may cause dead +// locks and/or lose of data updated in the uppermost opened but never properly +// closed transaction level. +// +// BeginTransactionStmt = "BEGIN" "TRANSACTION" . +// +// For example +// +// BEGIN TRANSACTION; +// INSERT INTO foo VALUES (42, 3.14); +// INSERT INTO foo VALUES (-1, 2.78); +// COMMIT; +// +// Mandatory transactions +// +// A database cannot be updated (mutated) outside of a transaction. Statements +// requiring a transaction +// +// ALTER TABLE +// COMMIT +// CREATE INDEX +// CREATE TABLE +// DELETE FROM +// DROP INDEX +// DROP TABLE +// INSERT INTO +// ROLLBACK +// TRUNCATE TABLE +// UPDATE +// +// A database is effectively read only outside of a transaction. Statements not +// requiring a transaction +// +// BEGIN TRANSACTION +// SELECT FROM +// +// COMMIT +// +// The commit statement closes the innermost transaction nesting level. If +// that's the outermost level then the updates to the DB made by the +// transaction are atomically made persistent. +// +// CommitStmt = "COMMIT" . +// +// For example +// +// BEGIN TRANSACTION; +// INSERT INTO AccountA (Amount) VALUES ($1); +// INSERT INTO AccountB (Amount) VALUES (-$1); +// COMMIT; +// +// CREATE INDEX +// +// Create index statements create new indices. Index is a named projection of +// ordered values of a table column to the respective records. As a special +// case the id() of the record can be indexed. Index name must not be the same +// as any of the existing tables and it also cannot be the same as of any +// column name of the table the index is on. +// +// CreateIndexStmt = "CREATE" [ "UNIQUE" ] "INDEX" [ "IF" "NOT" "EXISTS" ] +// IndexName "ON" TableName "(" ExpressionList ")" . +// +// For example +// +// BEGIN TRANSACTION; +// CREATE TABLE Orders (CustomerID int, Date time); +// CREATE INDEX OrdersID ON Orders (id()); +// CREATE INDEX OrdersDate ON Orders (Date); +// CREATE TABLE Items (OrderID int, ProductID int, Qty int); +// CREATE INDEX ItemsOrderID ON Items (OrderID); +// COMMIT; +// +// Now certain SELECT statements may use the indices to speed up joins and/or +// to speed up record set filtering when the WHERE clause is used; or the +// indices might be used to improve the performance when the ORDER BY clause is +// present. +// +// The UNIQUE modifier requires the indexed values tuple to be index-wise +// unique or have all values NULL. +// +// The optional IF NOT EXISTS clause makes the statement a no operation if the +// index already exists. +// +// Simple index +// +// A simple index consists of only one expression which must be either a column +// name or the built-in id(). +// +// Expression list index +// +// A more complex and more general index is one that consists of more than one +// expression or its single expression does not qualify as a simple index. In +// this case the type of all expressions in the list must be one of the non +// blob-like types. +// +// Note: Blob-like types are blob, bigint, bigrat, time and duration. +// +// CREATE TABLE +// +// Create table statements create new tables. A column definition declares the +// column name and type. Table names and column names are case sensitive. +// Neither a table or an index of the same name may exist in the DB. +// +// CreateTableStmt = "CREATE" "TABLE" [ "IF" "NOT" "EXISTS" ] TableName +// "(" ColumnDef { "," ColumnDef } [ "," ] ")" . +// +// ColumnDef = ColumnName Type [ "NOT" "NULL" | Expression ] [ "DEFAULT" Expression ] . +// ColumnName = identifier . +// TableName = identifier . +// +// For example +// +// BEGIN TRANSACTION; +// CREATE TABLE department ( +// DepartmentID int, +// DepartmentName string, +// ); +// CREATE TABLE employee ( +// LastName string, +// DepartmentID int, +// ); +// COMMIT; +// +// The optional IF NOT EXISTS clause makes the statement a no operation if the +// table already exists. +// +// The optional constraint clause has two forms. The first one is found in many +// SQL dialects. +// +// BEGIN TRANSACTION; +// CREATE TABLE department ( +// DepartmentID int, +// DepartmentName string NOT NULL, +// ); +// COMMIT; +// +// This form prevents the data in column DepartmentName to be NULL. +// +// The second form allows an arbitrary boolean expression to be used to +// validate the column. If the value of the expression is true then the +// validation succeeded. If the value of the expression is false or NULL then +// the validation fails. If the value of the expression is not of type bool an +// error occurs. +// +// BEGIN TRANSACTION; +// CREATE TABLE department ( +// DepartmentID int, +// DepartmentName string DepartmentName IN ("HQ", "R/D", "Lab", "HR"), +// ); +// COMMIT; +// +// BEGIN TRANSACTION; +// CREATE TABLE t ( +// TimeStamp time TimeStamp < now() && since(TimeStamp) < duration("10s"), +// Event string Event != "" && Event like "[0-9]+:[ \t]+.*", +// ); +// COMMIT; +// +// The optional DEFAULT clause is an expression which, if present, is +// substituted instead of a NULL value when the colum is assigned a value. +// +// BEGIN TRANSACTION; +// CREATE TABLE department ( +// DepartmentID int, +// DepartmentName string DepartmentName IN ("HQ", "R/D", "Lab", "HR") DEFAULT "HQ", +// ); +// COMMIT; +// +// Note that the constraint and/or default expressions may refer to other +// columns by name: +// +// BEGIN TRANSACTION; +// CREATE TABLE t ( +// a int, +// b int b > a && b < c DEFAULT (a+c)/2, +// c int, +// ); +// COMMIT; +// +// +// Constraints and defaults +// +// When a table row is inserted by the INSERT INTO statement or when a table +// row is updated by the UPDATE statement, the order of operations is as +// follows: +// +// 1. The new values of the affected columns are set and the values of all the +// row columns become the named values which can be referred to in default +// expressions evaluated in step 2. +// +// 2. If any row column value is NULL and the DEFAULT clause is present in the +// column's definition, the default expression is evaluated and its value is +// set as the respective column value. +// +// 3. The values, potentially updated, of row columns become the named values +// which can be referred to in constraint expressions evaluated during step 4. +// +// 4. All row columns which definition has the constraint clause present will +// have that constraint checked. If any constraint violation is detected, the +// overall operation fails and no changes to the table are made. +// +// DELETE FROM +// +// Delete from statements remove rows from a table, which must exist. +// +// DeleteFromStmt = "DELETE" "FROM" TableName [ WhereClause ] . +// +// For example +// +// BEGIN TRANSACTION; +// DELETE FROM DepartmentID +// WHERE DepartmentName == "Ponies"; +// COMMIT; +// +// If the WHERE clause is not present then all rows are removed and the +// statement is equivalent to the TRUNCATE TABLE statement. +// +// DROP INDEX +// +// Drop index statements remove indices from the DB. The index must exist. +// +// DropIndexStmt = "DROP" "INDEX" [ "IF" "EXISTS" ] IndexName . +// IndexName = identifier . +// +// For example +// +// BEGIN TRANSACTION; +// DROP INDEX ItemsOrderID; +// COMMIT; +// +// The optional IF EXISTS clause makes the statement a no operation if the +// index does not exist. +// +// DROP TABLE +// +// Drop table statements remove tables from the DB. The table must exist. +// +// DropTableStmt = "DROP" "TABLE" [ "IF" "EXISTS" ] TableName . +// +// For example +// +// BEGIN TRANSACTION; +// DROP TABLE Inventory; +// COMMIT; +// +// The optional IF EXISTS clause makes the statement a no operation if the +// table does not exist. +// +// INSERT INTO +// +// Insert into statements insert new rows into tables. New rows come from +// literal data, if using the VALUES clause, or are a result of select +// statement. In the later case the select statement is fully evaluated before +// the insertion of any rows is performed, allowing to insert values calculated +// from the same table rows are to be inserted into. If the ColumnNameList part +// is omitted then the number of values inserted in the row must be the same as +// are columns in the table. If the ColumnNameList part is present then the +// number of values per row must be same as the same number of column names. +// All other columns of the record are set to NULL. The type of the value +// assigned to a column must be the same as is the column's type or the value +// must be NULL. +// +// InsertIntoStmt = "INSERT" "INTO" TableName [ "(" ColumnNameList ")" ] ( Values | SelectStmt ) . +// +// ColumnNameList = ColumnName { "," ColumnName } [ "," ] . +// Values = "VALUES" "(" ExpressionList ")" { "," "(" ExpressionList ")" } [ "," ] . +// +// For example +// +// BEGIN TRANSACTION; +// INSERT INTO department (DepartmentID) VALUES (42); +// +// INSERT INTO department ( +// DepartmentName, +// DepartmentID, +// ) +// VALUES ( +// "R&D", +// 42, +// ); +// +// INSERT INTO department VALUES +// (42, "R&D"), +// (17, "Sales"), +// ; +// COMMIT; +// +// BEGIN TRANSACTION; +// INSERT INTO department (DepartmentName, DepartmentID) +// SELECT DepartmentName+"/headquarters", DepartmentID+1000 +// FROM department; +// COMMIT; +// +// If any of the columns of the table were defined using the optional +// constraints clause or the optional defaults clause then those are processed +// on a per row basis. The details are discussed in the "Constraints and +// defaults" chapter below the CREATE TABLE statement documentation. +// +// Explain statement +// +// Explain statement produces a recordset consisting of lines of text which +// describe the execution plan of a statement, if any. +// +// ExplainStmt = "EXPLAIN" Statement . +// +// For example, the QL tool treats the explain statement specially and outputs +// the joined lines: +// +// $ ql 'create table t(i int); create table u(j int)' +// $ ql 'explain select * from t, u where t.i > 42 && u.j < 314' +// ┌Compute Cartesian product of +// │ ┌Iterate all rows of table "t" +// │ └Output field names ["i"] +// │ ┌Iterate all rows of table "u" +// │ └Output field names ["j"] +// └Output field names ["t.i" "u.j"] +// ┌Filter on t.i > 42 && u.j < 314 +// │Possibly useful indices +// │CREATE INDEX xt_i ON t(i); +// │CREATE INDEX xu_j ON u(j); +// └Output field names ["t.i" "u.j"] +// $ ql 'CREATE INDEX xt_i ON t(i); CREATE INDEX xu_j ON u(j);' +// $ ql 'explain select * from t, u where t.i > 42 && u.j < 314' +// ┌Compute Cartesian product of +// │ ┌Iterate all rows of table "t" using index "xt_i" where i > 42 +// │ └Output field names ["i"] +// │ ┌Iterate all rows of table "u" using index "xu_j" where j < 314 +// │ └Output field names ["j"] +// └Output field names ["t.i" "u.j"] +// $ ql 'explain select * from t where i > 12 and i between 10 and 20 and i < 42' +// ┌Iterate all rows of table "t" using index "xt_i" where i > 12 && i <= 20 +// └Output field names ["i"] +// $ +// +// The explanation may aid in uderstanding how a statement/query would be +// executed and if indices are used as expected - or which indices may possibly +// improve the statement performance. The create index statements above were +// directly copy/pasted in the terminal from the suggestions provided by the +// filter recordset pipeline part returned by the explain statement. +// +// If the statement has nothing special in its plan, the result is the original +// statement. +// +// $ ql 'explain delete from t where 42 < i' +// DELETE FROM t WHERE i > 42; +// $ +// +// To get an explanation of the select statement of the IN predicate, use the EXPLAIN +// statement with that particular select statement. +// +// $ ql 'explain select * from t where i in (select j from u where j > 0)' +// ┌Iterate all rows of table "t" +// └Output field names ["i"] +// ┌Filter on i IN (SELECT j FROM u WHERE j > 0;) +// └Output field names ["i"] +// $ ql 'explain select j from u where j > 0' +// ┌Iterate all rows of table "u" using index "xu_j" where j > 0 +// └Output field names ["j"] +// $ +// +// ROLLBACK +// +// The rollback statement closes the innermost transaction nesting level +// discarding any updates to the DB made by it. If that's the outermost level +// then the effects on the DB are as if the transaction never happened. +// +// RollbackStmt = "ROLLBACK" . +// +// For example +// +// // First statement list +// BEGIN TRANSACTION +// SELECT * INTO tmp FROM foo; +// INSERT INTO tmp SELECT * from bar; +// SELECT * from tmp; +// +// The (temporary) record set from the last statement is returned and can be +// processed by the client. +// +// // Second statement list +// ROLLBACK; +// +// In this case the rollback is the same as 'DROP TABLE tmp;' but it can be a +// more complex operation. +// +// SELECT FROM +// +// Select from statements produce recordsets. The optional DISTINCT modifier +// ensures all rows in the result recordset are unique. Either all of the +// resulting fields are returned ('*') or only those named in FieldList. +// +// RecordSetList is a list of table names or parenthesized select statements, +// optionally (re)named using the AS clause. +// +// The result can be filtered using a WhereClause and orderd by the OrderBy +// clause. +// +// SelectStmt = "SELECT" [ "DISTINCT" ] ( "*" | FieldList ) "FROM" RecordSetList +// [ JoinClause ] [ WhereClause ] [ GroupByClause ] [ OrderBy ] [ Limit ] [ Offset ]. +// +// JoinClause = ( "LEFT" | "RIGHT" | "FULL" ) [ "OUTER" ] "JOIN" RecordSet "ON" Expression . +// +// RecordSet = ( TableName | "(" SelectStmt [ ";" ] ")" ) [ "AS" identifier ] . +// RecordSetList = RecordSet { "," RecordSet } [ "," ] . +// +// For example +// +// SELECT * FROM Stock; +// +// SELECT DepartmentID +// FROM department +// WHERE DepartmentID == 42 +// ORDER BY DepartmentName; +// +// SELECT employee.LastName +// FROM department, employee +// WHERE department.DepartmentID == employee.DepartmentID +// ORDER BY DepartmentID; +// +// If Recordset is a nested, parenthesized SelectStmt then it must be given a +// name using the AS clause if its field are to be accessible in expressions. +// +// SELECT a.b, c.d +// FROM +// x AS a, +// ( +// SELECT * FROM y; +// ) AS c +// WHERE a.e > c.e; +// +// Fields naming rules +// +// A field is an named expression. Identifiers, not used as a type in +// conversion or a function name in the Call clause, denote names of (other) +// fields, values of which should be used in the expression. +// +// Field = Expression [ "AS" identifier ] . +// +// The expression can be named using the AS clause. If the AS clause is not +// present and the expression consists solely of a field name, then that field +// name is used as the name of the resulting field. Otherwise the field is +// unnamed. +// +// For example +// +// SELECT 314, 42 as AUQLUE, DepartmentID, DepartmentID+1000, LastName as Name from employee; +// // Fields are []string{"", "AUQLUE", "DepartmentID", "", "Name"} +// +// The SELECT statement can optionally enumerate the desired/resulting fields +// in a list. +// +// FieldList = Field { "," Field } [ "," ] . +// +// No two identical field names can appear in the list. +// +// SELECT DepartmentID, LastName, DepartmentID from employee; +// // duplicate field name "DepartmentID" +// +// SELECT DepartmentID, LastName, DepartmentID as ID2 from employee; +// // works +// +// When more than one record set is used in the FROM clause record set list, +// the result record set field names are rewritten to be qualified using +// the record set names. +// +// SELECT * FROM employee, department; +// // Fields are []string{"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +// +// If a particular record set doesn't have a name, its respective fields became +// unnamed. +// +// SELECT * FROM employee as e, ( SELECT * FROM department); +// // Fields are []string{"e.LastName", "e.DepartmentID", "", "" +// +// SELECT * FROM employee AS e, ( SELECT * FROM department) AS d; +// // Fields are []string{"e.LastName", "e.DepartmentID", "d.DepartmentID", "d.DepartmentName" +// +// Outer joins +// +// The optional JOIN clause, for example +// +// SELECT * +// FROM a +// LEFT OUTER JOIN b ON expr; +// +// is mostly equal to +// +// SELECT * +// FROM a, b +// WHERE expr; +// +// except that the rows from a which, when they appear in the cross join, never +// made expr to evaluate to true, are combined with a virtual row from b, +// containing all nulls, and added to the result set. For the RIGHT JOIN +// variant the discussed rules are used for rows from b not satisfying expr == +// true and the virtual, all-null row "comes" from a. The FULL JOIN adds the +// respective rows which would be otherwise provided by the separate executions +// of the LEFT JOIN and RIGHT JOIN variants. For more thorough OUTER JOIN +// discussion please see the Wikipedia article at [10]. +// +// Recordset ordering +// +// Resultins rows of a SELECT statement can be optionally ordered by the ORDER +// BY clause. Collating proceeds by considering the expressions in the +// expression list left to right until a collating order is determined. Any +// possibly remaining expressions are not evaluated. +// +// OrderBy = "ORDER" "BY" ExpressionList [ "ASC" | "DESC" ] . +// +// All of the expression values must yield an ordered type or NULL. Ordered +// types are defined in "Comparison operators". Collating of elements having a +// NULL value is different compared to what the comparison operators yield in +// expression evaluation (NULL result instead of a boolean value). +// +// Below, T denotes a non NULL value of any QL type. +// +// NULL < T +// +// NULL collates before any non NULL value (is considered smaller than T). +// +// NULL == NULL +// +// Two NULLs have no collating order (are considered equal). +// +// Recordset filtering +// +// The WHERE clause restricts records considered by some statements, like +// SELECT FROM, DELETE FROM, or UPDATE. +// +// expression value consider the record +// ---------------- ------------------- +// true yes +// false or NULL no +// +// It is an error if the expression evaluates to a non null value of non bool +// type. +// +// WhereClause = "WHERE" Expression . +// +// Recordset grouping +// +// The GROUP BY clause is used to project rows having common values into a +// smaller set of rows. +// +// For example +// +// SELECT Country, sum(Qty) FROM Sales GROUP BY Country; +// +// SELECT Country, Product FROM Sales GROUP BY Country, Product; +// +// SELECT DISTINCT Country, Product FROM Sales; +// +// Using the GROUP BY without any aggregate functions in the selected fields is +// in certain cases equal to using the DISTINCT modifier. The last two examples +// above produce the same resultsets. +// +// GroupByClause = "GROUP BY" ColumnNameList . +// +// Skipping records +// +// The optional OFFSET clause allows to ignore first N records. For example +// +// SELECT * FROM t OFFSET 10; +// +// The above will produce only rows 11, 12, ... of the record set, if they +// exist. The value of the expression must a non negative integer, but not +// bigint or duration. +// +// Offset = "OFFSET" Expression . +// +// Limiting the result set size +// +// The optional LIMIT clause allows to ignore all but first N records. For +// example +// +// SELECT * FROM t LIMIT 10; +// +// The above will return at most the first 10 records of the record set. The +// value of the expression must a non negative integer, but not bigint or +// duration. +// +// Limit = "Limit" Expression . +// +// The LIMIT and OFFSET clauses can be combined. For example +// +// SELECT * FROM t LIMIT 5 OFFSET 3; +// +// Considering table t has, say 10 records, the above will produce only records +// 4 - 8. +// +// #1: Ignore 1/3 +// #2: Ignore 2/3 +// #3: Ignore 3/3 +// #4: Return 1/5 +// #5: Return 2/5 +// #6: Return 3/5 +// #7: Return 4/5 +// #8: Return 5/5 +// +// After returning record #8, no more result rows/records are computed. +// +// Select statement evaluation order +// +// 1. The FROM clause is evaluated, producing a Cartesian product of its source +// record sets (tables or nested SELECT statements). +// +// 2. If present, the JOIN cluase is evaluated on the result set of the +// previous evaluation and the recordset specified by the JOIN clause. (... +// JOIN Recordset ON ...) +// +// 3. If present, the WHERE clause is evaluated on the result set of the +// previous evaluation. +// +// 4. If present, the GROUP BY clause is evaluated on the result set of the +// previous evaluation(s). +// +// 5. The SELECT field expressions are evaluated on the result set of the +// previous evaluation(s). +// +// 6. If present, the DISTINCT modifier is evaluated on the result set of the +// previous evaluation(s). +// +// 7. If present, the ORDER BY clause is evaluated on the result set of the +// previous evaluation(s). +// +// 8. If present, the OFFSET clause is evaluated on the result set of the +// previous evaluation(s). The offset expression is evaluated once for the +// first record produced by the previous evaluations. +// +// 9. If present, the LIMIT clause is evaluated on the result set of the +// previous evaluation(s). The limit expression is evaluated once for the first +// record produced by the previous evaluations. +// +// +// TRUNCATE TABLE +// +// Truncate table statements remove all records from a table. The table must +// exist. +// +// TruncateTableStmt = "TRUNCATE" "TABLE" TableName . +// +// For example +// +// BEGIN TRANSACTION +// TRUNCATE TABLE department; +// COMMIT; +// +// UPDATE +// +// Update statements change values of fields in rows of a table. +// +// UpdateStmt = "UPDATE" TableName [ "SET" ] AssignmentList [ WhereClause ] . +// +// AssignmentList = Assignment { "," Assignment } [ "," ] . +// Assignment = ColumnName "=" Expression . +// +// For example +// +// BEGIN TRANSACTION +// UPDATE department +// DepartmentName = DepartmentName + " dpt.", +// DepartmentID = 1000+DepartmentID, +// WHERE DepartmentID < 1000; +// COMMIT; +// +// Note: The SET clause is optional. +// +// If any of the columns of the table were defined using the optional +// constraints clause or the optional defaults clause then those are processed +// on a per row basis. The details are discussed in the "Constraints and +// defaults" chapter below the CREATE TABLE statement documentation. +// +// System Tables +// +// To allow to query for DB meta data, there exist specially named tables, some +// of them being virtual. +// +// Note: Virtual system tables may have fake table-wise unique but meaningless +// and unstable record IDs. Do not apply the built-in id() to any system table. +// +// Tables Table +// +// The table __Table lists all tables in the DB. The schema is +// +// CREATE TABLE __Table (Name string, Schema string); +// +// The Schema column returns the statement to (re)create table Name. This table +// is virtual. +// +// Columns Table +// +// The table __Colum lists all columns of all tables in the DB. The schema is +// +// CREATE TABLE __Column (TableName string, Ordinal int, Name string, Type string); +// +// The Ordinal column defines the 1-based index of the column in the record. +// This table is virtual. +// +// Columns2 Table +// +// The table __Colum2 lists all columns of all tables in the DB which have the +// constraint NOT NULL or which have a constraint expression defined or which +// have a default expression defined. The schema is +// +// CREATE TABLE __Column2 (TableName string, Name string, NotNull bool, ConstraintExpr string, DefaultExpr string) +// +// It's possible to obtain a consolidated recordset for all properties of all +// DB columns using +// +// SELECT +// __Column.TableName, __Column.Ordinal, __Column.Name, __Column.Type, +// __Column2.NotNull, __Column2.ConstraintExpr, __Column2.DefaultExpr, +// FROM __Column +// LEFT JOIN __Column2 +// ON __Column.TableName == __Column2.TableName && __Column.Name == __Column2.Name +// ORDER BY __Column.TableName, __Column.Ordinal; +// +// The Name column is the column name in TableName. +// +// Indices table +// +// The table __Index lists all indices in the DB. The schema is +// +// CREATE TABLE __Index (TableName string, ColumnName string, Name string, IsUnique bool); +// +// The IsUnique columns reflects if the index was created using the optional +// UNIQUE clause. This table is virtual. +// +// Built-in functions +// +// Built-in functions are predeclared. +// +// Average +// +// The built-in aggregate function avg returns the average of values of an +// expression. Avg ignores NULL values, but returns NULL if all values of a +// column are NULL or if avg is applied to an empty record set. +// +// func avg(e numeric) typeof(e) +// +// The column values must be of a numeric type. +// +// SELECT salesperson, avg(sales) FROM salesforce GROUP BY salesperson; +// +// Contains +// +// The built-in function contains returns true if substr is within s. +// +// func contains(s, substr string) bool +// +// If any argument to contains is NULL the result is NULL. +// +// Count +// +// The built-in aggregate function count returns how many times an expression +// has a non NULL values or the number of rows in a record set. Note: count() +// returns 0 for an empty record set. +// +// func count() int // The number of rows in a record set. +// func count(*) int // Equivalent to count(). +// func count(e expression) int // The number of cases where the expression value is not NULL. +// +// For example +// +// SELECT count() FROM department; // # of rows +// +// SELECT count(*) FROM department; // # of rows +// +// SELECT count(DepartmentID) FROM department; // # of records with non NULL field DepartmentID +// +// SELECT count()-count(DepartmentID) FROM department; // # of records with NULL field DepartmentID +// +// SELECT count(foo+bar*3) AS y FROM t; // # of cases where 'foo+bar*3' is non NULL +// +// Date +// +// Date returns the time corresponding to +// +// yyyy-mm-dd hh:mm:ss + nsec nanoseconds +// +// in the appropriate zone for that time in the given location. +// +// The month, day, hour, min, sec, and nsec values may be outside their usual +// ranges and will be normalized during the conversion. For example, October 32 +// converts to November 1. +// +// A daylight savings time transition skips or repeats times. For example, in +// the United States, March 13, 2011 2:15am never occurred, while November 6, +// 2011 1:15am occurred twice. In such cases, the choice of time zone, and +// therefore the time, is not well-defined. Date returns a time that is correct +// in one of the two zones involved in the transition, but it does not +// guarantee which. +// +// func date(year, month, day, hour, min, sec, nsec int, loc string) time +// +// A location maps time instants to the zone in use at that time. Typically, +// the location represents the collection of time offsets in use in a +// geographical area, such as "CEST" and "CET" for central Europe. "local" +// represents the system's local time zone. "UTC" represents Universal +// Coordinated Time (UTC). +// +// The month specifies a month of the year (January = 1, ...). +// +// If any argument to date is NULL the result is NULL. +// +// Day +// +// The built-in function day returns the day of the month specified by t. +// +// func day(t time) int +// +// If the argument to day is NULL the result is NULL. +// +// Format time +// +// The built-in function formatTime returns a textual representation of the +// time value formatted according to layout, which defines the format by +// showing how the reference time, +// +// Mon Jan 2 15:04:05 -0700 MST 2006 +// +// would be displayed if it were the value; it serves as an example of the +// desired output. The same display rules will then be applied to the time +// value. +// +// func formatTime(t time, layout string) string +// +// If any argument to formatTime is NULL the result is NULL. +// +// NOTE: The string value of the time zone, like "CET" or "ACDT", is dependent +// on the time zone of the machine the function is run on. For example, if the +// t value is in "CET", but the machine is in "ACDT", instead of "CET" the +// result is "+0100". This is the same what Go (time.Time).String() returns and +// in fact formatTime directly calls t.String(). +// +// formatTime(date(2006, 1, 2, 15, 4, 5, 999999999, "CET")) +// +// returns +// +// 2006-01-02 15:04:05.999999999 +0100 CET +// +// on a machine in the CET time zone, but may return +// +// 2006-01-02 15:04:05.999999999 +0100 +0100 +// +// on a machine in the ACDT zone. The time value is in both cases the same so +// its ordering and comparing is correct. Only the display value can differ. +// +// Format numbers +// +// The built-in functions formatFloat and formatInt format numbers +// to strings using go's number format functions in the `strconv` package. For +// all three functions, only the first argument is mandatory. The default values +// of the rest are shown in the examples. If the first argument is NULL, the +// result is NULL. +// +// formatFloat(43.2[, 'g', -1, 64]) string +// +// returns +// +// "43.2" +// +// formatInt(-42[, 10]) string +// +// returns +// +// "-42" +// +// formatInt(uint32(42)[, 10]) string +// +// returns +// +// "42" +// +// Unlike the `strconv` equivalent, the formatInt function handles all integer +// types, both signed and unsigned. +// +// HasPrefix +// +// The built-in function hasPrefix tests whether the string s begins with prefix. +// +// func hasPrefix(s, prefix string) bool +// +// If any argument to hasPrefix is NULL the result is NULL. +// +// HasSuffix +// +// The built-in function hasSuffix tests whether the string s ends with suffix. +// +// func hasSuffix(s, suffix string) bool +// +// If any argument to hasSuffix is NULL the result is NULL. +// +// Hour +// +// The built-in function hour returns the hour within the day specified by t, +// in the range [0, 23]. +// +// func hour(t time) int +// +// If the argument to hour is NULL the result is NULL. +// +// Hours +// +// The built-in function hours returns the duration as a floating point number +// of hours. +// +// func hours(d duration) float +// +// If the argument to hours is NULL the result is NULL. +// +// Record id +// +// The built-in function id takes zero or one arguments. If no argument is +// provided, id() returns a table-unique automatically assigned numeric +// identifier of type int. Ids of deleted records are not reused unless the DB +// becomes completely empty (has no tables). +// +// func id() int +// +// For example +// +// SELECT id(), LastName +// FROM employee; +// +// If id() without arguments is called for a row which is not a table record +// then the result value is NULL. +// +// For example +// +// SELECT id(), e.LastName, e.DepartmentID, d.DepartmentID +// FROM +// employee AS e, +// department AS d, +// WHERE e.DepartmentID == d.DepartmentID; +// // Will always return NULL in first field. +// +// SELECT e.ID, e.LastName, e.DepartmentID, d.DepartmentID +// FROM +// (SELECT id() AS ID, LastName, DepartmentID FROM employee) AS e, +// department as d, +// WHERE e.DepartmentID == d.DepartmentID; +// // Will work. +// +// If id() has one argument it must be a table name of a table in a cross join. +// +// For example +// +// SELECT * +// FROM foo, bar +// WHERE bar.fooID == id(foo) +// ORDER BY id(foo); +// +// Length +// +// The built-in function len takes a string argument and returns the lentgh of +// the string in bytes. +// +// func len(s string) int +// +// The expression len(s) is constant if s is a string constant. +// +// If the argument to len is NULL the result is NULL. +// +// Maximum +// +// The built-in aggregate function max returns the largest value of an +// expression in a record set. Max ignores NULL values, but returns NULL if +// all values of a column are NULL or if max is applied to an empty record set. +// +// func max(e expression) typeof(e) // The largest value of the expression. +// +// The expression values must be of an ordered type. +// +// For example +// +// SELECT department, max(sales) FROM t GROUP BY department; +// +// Minimum +// +// The built-in aggregate function min returns the smallest value of an +// expression in a record set. Min ignores NULL values, but returns NULL if +// all values of a column are NULL or if min is applied to an empty record set. +// +// func min(e expression) typeof(e) // The smallest value of the expression. +// +// For example +// +// SELECT a, min(b) FROM t GROUP BY a; +// +// The column values must be of an ordered type. +// +// Minute +// +// The built-in function minute returns the minute offset within the hour +// specified by t, in the range [0, 59]. +// +// func minute(t time) int +// +// If the argument to minute is NULL the result is NULL. +// +// Minutes +// +// The built-in function minutes returns the duration as a floating point +// number of minutes. +// +// func minutes(d duration) float +// +// If the argument to minutes is NULL the result is NULL. +// +// Month +// +// The built-in function month returns the month of the year specified by t +// (January = 1, ...). +// +// func month(t time) int +// +// If the argument to month is NULL the result is NULL. +// +// Nanosecond +// +// The built-in function nanosecond returns the nanosecond offset within the +// second specified by t, in the range [0, 999999999]. +// +// func nanosecond(t time) int +// +// If the argument to nanosecond is NULL the result is NULL. +// +// Nanoseconds +// +// The built-in function nanoseconds returns the duration as an integer +// nanosecond count. +// +// func nanoseconds(d duration) float +// +// If the argument to nanoseconds is NULL the result is NULL. +// +// Now +// +// The built-in function now returns the current local time. +// +// func now() time +// +// Parse time +// +// The built-in function parseTime parses a formatted string and returns the +// time value it represents. The layout defines the format by showing how the +// reference time, +// +// Mon Jan 2 15:04:05 -0700 MST 2006 +// +// would be interpreted if it were the value; it serves as an example of the +// input format. The same interpretation will then be made to the input string. +// +// Elements omitted from the value are assumed to be zero or, when zero is +// impossible, one, so parsing "3:04pm" returns the time corresponding to Jan +// 1, year 0, 15:04:00 UTC (note that because the year is 0, this time is +// before the zero Time). Years must be in the range 0000..9999. The day of the +// week is checked for syntax but it is otherwise ignored. +// +// In the absence of a time zone indicator, parseTime returns a time in UTC. +// +// When parsing a time with a zone offset like -0700, if the offset corresponds +// to a time zone used by the current location, then parseTime uses that +// location and zone in the returned time. Otherwise it records the time as +// being in a fabricated location with time fixed at the given zone offset. +// +// When parsing a time with a zone abbreviation like MST, if the zone +// abbreviation has a defined offset in the current location, then that offset +// is used. The zone abbreviation "UTC" is recognized as UTC regardless of +// location. If the zone abbreviation is unknown, Parse records the time as +// being in a fabricated location with the given zone abbreviation and a zero +// offset. This choice means that such a time can be parses and reformatted +// with the same layout losslessly, but the exact instant used in the +// representation will differ by the actual zone offset. To avoid such +// problems, prefer time layouts that use a numeric zone offset. +// +// func parseTime(layout, value string) time +// +// If any argument to parseTime is NULL the result is NULL. +// +// Second +// +// The built-in function second returns the second offset within the minute +// specified by t, in the range [0, 59]. +// +// func second(t time) int +// +// If the argument to second is NULL the result is NULL. +// +// Seconds +// +// The built-in function seconds returns the duration as a floating point +// number of seconds. +// +// func seconds(d duration) float +// +// If the argument to seconds is NULL the result is NULL. +// +// Since +// +// The built-in function since returns the time elapsed since t. It is +// shorthand for now()-t. +// +// func since(t time) duration +// +// If the argument to since is NULL the result is NULL. +// +// Sum +// +// The built-in aggregate function sum returns the sum of values of an +// expression for all rows of a record set. Sum ignores NULL values, but +// returns NULL if all values of a column are NULL or if sum is applied to an +// empty record set. +// +// func sum(e expression) typeof(e) // The sum of the values of the expression. +// +// The column values must be of a numeric type. +// +// SELECT salesperson, sum(sales) FROM salesforce GROUP BY salesperson; +// +// Time in a specific zone +// +// The built-in function timeIn returns t with the location information set to +// loc. For discussion of the loc argument please see date(). +// +// func timeIn(t time, loc string) time +// +// If any argument to timeIn is NULL the result is NULL. +// +// Weekday +// +// The built-in function weekday returns the day of the week specified by t. +// Sunday == 0, Monday == 1, ... +// +// func weekday(t time) int +// +// If the argument to weekday is NULL the result is NULL. +// +// Year +// +// The built-in function year returns the year in which t occurs. +// +// func year(t time) int +// +// If the argument to year is NULL the result is NULL. +// +// Year day +// +// The built-in function yearDay returns the day of the year specified by t, in +// the range [1,365] for non-leap years, and [1,366] in leap years. +// +// func yearDay(t time) int +// +// If the argument to yearDay is NULL the result is NULL. +// +// Manipulating complex numbers +// +// Three functions assemble and disassemble complex numbers. The built-in +// function complex constructs a complex value from a floating-point real and +// imaginary part, while real and imag extract the real and imaginary parts of +// a complex value. +// +// complex(realPart, imaginaryPart floatT) complexT +// real(complexT) floatT +// imag(complexT) floatT +// +// The type of the arguments and return value correspond. For complex, the two +// arguments must be of the same floating-point type and the return type is the +// complex type with the corresponding floating-point constituents: complex64 +// for float32, complex128 for float64. The real and imag functions together +// form the inverse, so for a complex value z, z == complex(real(z), imag(z)). +// +// If the operands of these functions are all constants, the return value is a +// constant. +// +// complex(2, -2) // complex128 +// complex(1.0, -1.4) // complex128 +// float32(math.Cos(math.Pi/2)) // float32 +// complex(5, float32(-x)) // complex64 +// imag(b) // float64 +// real(complex(5, float32(-x))) // float32 +// +// If any argument to any of complex, real, imag functions is NULL the result +// is NULL. +// +// Size guarantees +// +// For the numeric types, the following sizes are guaranteed +// +// type size in bytes +// +// byte, uint8, int8 1 +// uint16, int16 2 +// uint32, int32, float32 4 +// uint, uint64, int, int64, float64, complex64 8 +// complex128 16 +// +// License +// +// Portions of this specification page are modifications based on work[2] +// created and shared by Google[3] and used according to terms described in the +// Creative Commons 3.0 Attribution License[4]. +// +// This specification is licensed under the Creative Commons Attribution 3.0 +// License, and code is licensed under a BSD license[5]. +// +// References +// +// Links from the above documentation +// +// [1]: http://golang.org/ref/spec#Notation +// [2]: http://golang.org/ref/spec +// [3]: http://code.google.com/policies.html +// [4]: http://creativecommons.org/licenses/by/3.0/ +// [5]: http://golang.org/LICENSE +// [6]: http://golang.org/pkg/regexp/#Regexp.MatchString +// [7]: http://developer.mimer.com/validator/sql-reserved-words.tml +// [8]: http://godoc.org/github.com/cznic/zappy +// [9]: http://www.w3schools.com/sql/sql_default.asp +// [10]: http://en.wikipedia.org/wiki/Join_(SQL)#Outer_join +// +// Implementation details +// +// This section is not part of the specification. +// +// Indices +// +// WARNING: The implementation of indices is new and it surely needs more time +// to become mature. +// +// Indices are used currently used only by the WHERE clause. The following +// expression patterns of 'WHERE expression' are recognized and trigger index +// use. +// +// - WHERE c // For bool typed indexed column c +// - WHERE !c // For bool typed indexed column c +// - WHERE c relOp constExpr // For indexed column c +// - WHERE c relOp parameter // For indexed column c +// - WHERE parameter relOp c // For indexed column c +// - WHERE constExpr relOp c // For indexed column c +// +// The relOp is one of the relation operators <, <=, ==, >=, >. For the +// equality operator both operands must be of comparable types. For all other +// operators both operands must be of ordered types. The constant expression is +// a compile time constant expression. Some constant folding is still a TODO. +// Parameter is a QL parameter ($1 etc.). +// +// Query rewriting +// +// Consider tables t and u, both with an indexed field f. The WHERE expression +// doesn't comply with the above simple detected cases. +// +// SELECT * FROM t, u WHERE t.f < x && u.f < y; +// +// However, such query is now automatically rewritten to +// +// SELECT * FROM +// (SELECT * FROM t WHERE f < x), +// (SELECT * FROM u WHERE f < y); +// +// which will use both of the indices. The impact of using the indices can be +// substantial (cf. BenchmarkCrossJoin*) if the resulting rows have low +// "selectivity", ie. only few rows from both tables are selected by the +// respective WHERE filtering. +// +// Note: Existing QL DBs can be used and indices can be added to them. However, +// once any indices are present in the DB, the old QL versions cannot work with +// such DB anymore. +// +// Benchmarks +// +// Running a benchmark with -v (-test.v) outputs information about the scale +// used to report records/s and a brief description of the benchmark. For +// example +// +// $ go test -run NONE -bench 'SelectMem.*1e[23]' -v +// PASS +// BenchmarkSelectMem1kBx1e2 50000 67680 ns/op 1477537.05 MB/s +// --- BENCH: BenchmarkSelectMem1kBx1e2 +// all_test.go:310: +// ============================================================= +// NOTE: All benchmarks report records/s as 1000000 bytes/s. +// ============================================================= +// all_test.go:321: Having a table of 100 records, each of size 1kB, measure the performance of +// SELECT * FROM t; +// +// BenchmarkSelectMem1kBx1e3 5000 634819 ns/op 1575251.01 MB/s +// --- BENCH: BenchmarkSelectMem1kBx1e3 +// all_test.go:321: Having a table of 1000 records, each of size 1kB, measure the performance of +// SELECT * FROM t; +// +// ok github.com/cznic/ql 7.496s +// $ +// +// Running the full suite of benchmarks takes a lot of time. Use the -timeout +// flag to avoid them being killed after the default time limit (10 minutes). +package ql diff --git a/Godeps/_workspace/src/github.com/cznic/ql/driver.go b/Godeps/_workspace/src/github.com/cznic/ql/driver.go new file mode 100644 index 000000000..7aa69b4c1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/driver.go @@ -0,0 +1,528 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// database/sql/driver + +package ql + +import ( + "bytes" + "database/sql" + "database/sql/driver" + "errors" + "fmt" + "io" + "math/big" + "os" + "path/filepath" + "strings" + "sync" + "time" +) + +var ( + _ driver.Conn = (*driverConn)(nil) + _ driver.Driver = (*sqlDriver)(nil) + _ driver.Execer = (*driverConn)(nil) + _ driver.Queryer = (*driverConn)(nil) + _ driver.Result = (*driverResult)(nil) + _ driver.Rows = (*driverRows)(nil) + _ driver.Stmt = (*driverStmt)(nil) + _ driver.Tx = (*driverConn)(nil) + + txBegin = MustCompile("BEGIN TRANSACTION;") + txCommit = MustCompile("COMMIT;") + txRollback = MustCompile("ROLLBACK;") + + errNoResult = errors.New("query statement does not produce a result set (no top level SELECT)") +) + +type errList []error + +func (e *errList) append(err error) { + if err != nil { + *e = append(*e, err) + } +} + +func (e errList) error() error { + if len(e) == 0 { + return nil + } + + return e +} + +func (e errList) Error() string { + a := make([]string, len(e)) + for i, v := range e { + a[i] = v.Error() + } + return strings.Join(a, "\n") +} + +func params(args []driver.Value) []interface{} { + r := make([]interface{}, len(args)) + for i, v := range args { + r[i] = interface{}(v) + } + return r +} + +var ( + fileDriver = &sqlDriver{dbs: map[string]*driverDB{}} + fileDriverOnce sync.Once + memDriver = &sqlDriver{isMem: true, dbs: map[string]*driverDB{}} + memDriverOnce sync.Once +) + +// RegisterDriver registers a QL database/sql/driver[0] named "ql". The name +// parameter of +// +// sql.Open("ql", name) +// +// is interpreted as a path name to a named DB file which will be created if +// not present. The underlying QL database data are persisted on db.Close(). +// RegisterDriver can be safely called multiple times, it'll register the +// driver only once. +// +// The name argument can be optionally prefixed by "file://". In that case the +// prefix is stripped before interpreting it as a file name. +// +// The name argument can be optionally prefixed by "memory://". In that case +// the prefix is stripped before interpreting it as a name of a memory-only, +// volatile DB. +// +// [0]: http://golang.org/pkg/database/sql/driver/ +func RegisterDriver() { + fileDriverOnce.Do(func() { sql.Register("ql", fileDriver) }) +} + +// RegisterMemDriver registers a QL memory database/sql/driver[0] named +// "ql-mem". The name parameter of +// +// sql.Open("ql-mem", name) +// +// is interpreted as an unique memory DB name which will be created if not +// present. The underlying QL memory database data are not persisted on +// db.Close(). RegisterMemDriver can be safely called multiple times, it'll +// register the driver only once. +// +// [0]: http://golang.org/pkg/database/sql/driver/ +func RegisterMemDriver() { + memDriverOnce.Do(func() { sql.Register("ql-mem", memDriver) }) +} + +type driverDB struct { + db *DB + name string + refcount int +} + +func newDriverDB(db *DB, name string) *driverDB { + return &driverDB{db: db, name: name, refcount: 1} +} + +// sqlDriver implements the interface required by database/sql/driver. +type sqlDriver struct { + dbs map[string]*driverDB + isMem bool + mu sync.Mutex +} + +func (d *sqlDriver) lock() func() { + d.mu.Lock() + return d.mu.Unlock +} + +// Open returns a new connection to the database. The name is a string in a +// driver-specific format. +// +// Open may return a cached connection (one previously closed), but doing so is +// unnecessary; the sql package maintains a pool of idle connections for +// efficient re-use. +// +// The returned connection is only used by one goroutine at a time. +func (d *sqlDriver) Open(name string) (driver.Conn, error) { + if d != fileDriver && d != memDriver { + return nil, fmt.Errorf("open: unexpected/unsupported instance of driver.Driver: %p", d) + } + + switch { + case d == fileDriver && strings.HasPrefix(name, "file://"): + name = name[len("file://"):] + case d == fileDriver && strings.HasPrefix(name, "memory://"): + d = memDriver + name = name[len("memory://"):] + } + name = filepath.Clean(name) + if name == "" || name == "." || name == string(os.PathSeparator) { + return nil, fmt.Errorf("invalid DB name %q", name) + } + + defer d.lock()() + db := d.dbs[name] + if db == nil { + var err error + var db0 *DB + switch d.isMem { + case true: + db0, err = OpenMem() + default: + db0, err = OpenFile(name, &Options{CanCreate: true}) + } + if err != nil { + return nil, err + } + + db = newDriverDB(db0, name) + d.dbs[name] = db + return newDriverConn(d, db), nil + } + + db.refcount++ + return newDriverConn(d, db), nil +} + +// driverConn is a connection to a database. It is not used concurrently by +// multiple goroutines. +// +// Conn is assumed to be stateful. +type driverConn struct { + ctx *TCtx + db *driverDB + driver *sqlDriver + stop map[*driverStmt]struct{} + tnl int +} + +func newDriverConn(d *sqlDriver, ddb *driverDB) driver.Conn { + r := &driverConn{ + db: ddb, + driver: d, + stop: map[*driverStmt]struct{}{}, + } + return r +} + +// Prepare returns a prepared statement, bound to this connection. +func (c *driverConn) Prepare(query string) (driver.Stmt, error) { + list, err := Compile(query) + if err != nil { + return nil, err + } + + s := &driverStmt{conn: c, stmt: list} + c.stop[s] = struct{}{} + return s, nil +} + +// Close invalidates and potentially stops any current prepared statements and +// transactions, marking this connection as no longer in use. +// +// Because the sql package maintains a free pool of connections and only calls +// Close when there's a surplus of idle connections, it shouldn't be necessary +// for drivers to do their own connection caching. +func (c *driverConn) Close() error { + var err errList + for s := range c.stop { + err.append(s.Close()) + } + defer c.driver.lock()() + dbs, name := c.driver.dbs, c.db.name + v := dbs[name] + v.refcount-- + if v.refcount == 0 { + err.append(c.db.db.Close()) + delete(dbs, name) + } + return err.error() +} + +// Begin starts and returns a new transaction. +func (c *driverConn) Begin() (driver.Tx, error) { + if c.ctx == nil { + c.ctx = NewRWCtx() + } + + if _, _, err := c.db.db.Execute(c.ctx, txBegin); err != nil { + return nil, err + } + + c.tnl++ + return c, nil +} + +func (c *driverConn) Commit() error { + if c.tnl == 0 || c.ctx == nil { + return errCommitNotInTransaction + } + + if _, _, err := c.db.db.Execute(c.ctx, txCommit); err != nil { + return err + } + + c.tnl-- + if c.tnl == 0 { + c.ctx = nil + } + return nil +} + +func (c *driverConn) Rollback() error { + if c.tnl == 0 || c.ctx == nil { + return errRollbackNotInTransaction + } + + if _, _, err := c.db.db.Execute(c.ctx, txRollback); err != nil { + return err + } + + c.tnl-- + if c.tnl == 0 { + c.ctx = nil + } + return nil +} + +// Execer is an optional interface that may be implemented by a Conn. +// +// If a Conn does not implement Execer, the sql package's DB.Exec will first +// prepare a query, execute the statement, and then close the statement. +// +// Exec may return driver.ErrSkip. +func (c *driverConn) Exec(query string, args []driver.Value) (driver.Result, error) { + list, err := Compile(query) + if err != nil { + return nil, err + } + + return driverExec(c.db, c.ctx, list, args) +} + +func driverExec(db *driverDB, ctx *TCtx, list List, args []driver.Value) (driver.Result, error) { + if _, _, err := db.db.Execute(ctx, list, params(args)...); err != nil { + return nil, err + } + + if len(list.l) == 1 { + switch list.l[0].(type) { + case *createTableStmt, *dropTableStmt, *alterTableAddStmt, + *alterTableDropColumnStmt, *truncateTableStmt: + return driver.ResultNoRows, nil + } + } + + r := &driverResult{} + if ctx != nil { + r.lastInsertID, r.rowsAffected = ctx.LastInsertID, ctx.RowsAffected + } + return r, nil +} + +// Queryer is an optional interface that may be implemented by a Conn. +// +// If a Conn does not implement Queryer, the sql package's DB.Query will first +// prepare a query, execute the statement, and then close the statement. +// +// Query may return driver.ErrSkip. +func (c *driverConn) Query(query string, args []driver.Value) (driver.Rows, error) { + list, err := Compile(query) + if err != nil { + return nil, err + } + + return driverQuery(c.db, c.ctx, list, args) +} + +func driverQuery(db *driverDB, ctx *TCtx, list List, args []driver.Value) (driver.Rows, error) { + rss, _, err := db.db.Execute(ctx, list, params(args)...) + if err != nil { + return nil, err + } + + switch n := len(rss); n { + case 0: + return nil, errNoResult + case 1: + rs := rss[n-1] + if x, ok := rss[n-1].(recordset); ok { + x.tx = ctx + rs = x + } + return newdriverRows(rs), nil + default: + return nil, fmt.Errorf("query produced %d result sets, expected only one", n) + } +} + +// driverResult is the result of a query execution. +type driverResult struct { + lastInsertID int64 + rowsAffected int64 +} + +// LastInsertId returns the database's auto-generated ID after, for example, an +// INSERT into a table with primary key. +func (r *driverResult) LastInsertId() (int64, error) { // -golint + return r.lastInsertID, nil +} + +// RowsAffected returns the number of rows affected by the query. +func (r *driverResult) RowsAffected() (int64, error) { + return r.rowsAffected, nil +} + +// driverRows is an iterator over an executed query's results. +type driverRows struct { + rs Recordset + done chan int + rows chan interface{} +} + +func newdriverRows(rs Recordset) *driverRows { + r := &driverRows{ + rs: rs, + done: make(chan int), + rows: make(chan interface{}, 500), + } + go func() { + err := io.EOF + if e := r.rs.Do(false, func(data []interface{}) (bool, error) { + select { + case r.rows <- data: + return true, nil + case <-r.done: + return false, nil + } + }); e != nil { + err = e + } + + select { + case r.rows <- err: + case <-r.done: + } + }() + return r +} + +// Columns returns the names of the columns. The number of columns of the +// result is inferred from the length of the slice. If a particular column +// name isn't known, an empty string should be returned for that entry. +func (r *driverRows) Columns() []string { + f, _ := r.rs.Fields() + return f +} + +// Close closes the rows iterator. +func (r *driverRows) Close() error { + close(r.done) + return nil +} + +// Next is called to populate the next row of data into the provided slice. The +// provided slice will be the same size as the Columns() are wide. +// +// The dest slice may be populated only with a driver Value type, but excluding +// string. All string values must be converted to []byte. +// +// Next should return io.EOF when there are no more rows. +func (r *driverRows) Next(dest []driver.Value) error { + select { + case rx := <-r.rows: + switch x := rx.(type) { + case error: + return x + case []interface{}: + if g, e := len(x), len(dest); g != e { + return fmt.Errorf("field count mismatch: got %d, need %d", g, e) + } + + for i, xi := range x { + switch v := xi.(type) { + case nil, int64, float64, bool, []byte, time.Time: + dest[i] = v + case complex64, complex128, *big.Int, *big.Rat: + var buf bytes.Buffer + fmt.Fprintf(&buf, "%v", v) + dest[i] = buf.Bytes() + case int8: + dest[i] = int64(v) + case int16: + dest[i] = int64(v) + case int32: + dest[i] = int64(v) + case int: + dest[i] = int64(v) + case uint8: + dest[i] = int64(v) + case uint16: + dest[i] = int64(v) + case uint32: + dest[i] = int64(v) + case uint64: + dest[i] = int64(v) + case uint: + dest[i] = int64(v) + case time.Duration: + dest[i] = int64(v) + case string: + dest[i] = []byte(v) + default: + return fmt.Errorf("internal error 004") + } + } + return nil + default: + return fmt.Errorf("internal error 005") + } + case <-r.done: + return io.EOF + } +} + +// driverStmt is a prepared statement. It is bound to a driverConn and not used +// by multiple goroutines concurrently. +type driverStmt struct { + conn *driverConn + stmt List +} + +// Close closes the statement. +// +// As of Go 1.1, a Stmt will not be closed if it's in use by any queries. +func (s *driverStmt) Close() error { + delete(s.conn.stop, s) + return nil +} + +// NumInput returns the number of placeholder parameters. +// +// If NumInput returns >= 0, the sql package will sanity check argument counts +// from callers and return errors to the caller before the statement's Exec or +// Query methods are called. +// +// NumInput may also return -1, if the driver doesn't know its number of +// placeholders. In that case, the sql package will not sanity check Exec or +// Query argument counts. +func (s *driverStmt) NumInput() int { + if x := s.stmt; len(x.l) == 1 { + return x.params + } + + return -1 +} + +// Exec executes a query that doesn't return rows, such as an INSERT or UPDATE. +func (s *driverStmt) Exec(args []driver.Value) (driver.Result, error) { + c := s.conn + return driverExec(c.db, c.ctx, s.stmt, args) +} + +// Exec executes a query that may return rows, such as a SELECT. +func (s *driverStmt) Query(args []driver.Value) (driver.Rows, error) { + c := s.conn + return driverQuery(c.db, c.ctx, s.stmt, args) +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/driver/Makefile b/Godeps/_workspace/src/github.com/cznic/ql/driver/Makefile new file mode 100644 index 000000000..f1b64ee66 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/driver/Makefile @@ -0,0 +1,40 @@ +# Copyright (c) 2014 The ql Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all clean nuke + +all: editor + go install + go vet + golint . + make todo + +bench: all + go test -run NONE -bench . + +clean: + go clean + rm -f *~ + +cover: + t=$(shell tempfile) ; go test -coverprofile $$t && go tool cover -html $$t && unlink $$t + +editor: + go fmt + go test -i + go test + go build + +nuke: + go clean -i + +todo: + @grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* *.go || true + @grep -n TODO *.go || true + @grep -n BUG *.go || true + @grep -n println *.go || true + +later: + @grep -n LATER *.go || true + @grep -n MAYBE *.go || true diff --git a/Godeps/_workspace/src/github.com/cznic/ql/driver/all_test.go b/Godeps/_workspace/src/github.com/cznic/ql/driver/all_test.go new file mode 100644 index 000000000..db28d1ded --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/driver/all_test.go @@ -0,0 +1,202 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package driver + +import ( + "database/sql" + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +func Example_testFile() { + dir, err := ioutil.TempDir("", "ql-driver-test") + if err != nil { + return + } + + defer func() { + os.RemoveAll(dir) + }() + + db, err := sql.Open("ql", filepath.Join(dir, "ql.db")) + if err != nil { + return + } + + defer func() { + if err := db.Close(); err != nil { + return + } + + fmt.Println("OK") + }() + + tx, err := db.Begin() + if err != nil { + return + } + + if _, err := tx.Exec("CREATE TABLE t (Qty int, Name string);"); err != nil { + return + } + + result, err := tx.Exec(` + INSERT INTO t VALUES + ($1, $2), + ($3, $4), + ; + `, + 42, "foo", + 314, "bar", + ) + if err != nil { + return + } + + if err = tx.Commit(); err != nil { + return + } + + id, err := result.LastInsertId() + if err != nil { + return + } + + aff, err := result.RowsAffected() + if err != nil { + return + } + + fmt.Printf("LastInsertId %d, RowsAffected %d\n", id, aff) + + rows, err := db.Query("SELECT * FROM t;") + if err != nil { + return + } + + cols, err := rows.Columns() + if err != nil { + return + } + + fmt.Printf("Columns: %v\n", cols) + + var data struct { + Qty int + Name string + } + + for rows.Next() { + if err = rows.Scan(&data.Qty, &data.Name); err != nil { + rows.Close() + break + } + + fmt.Printf("%+v\n", data) + } + + if err = rows.Err(); err != nil { + return + } + + // Output: + // LastInsertId 2, RowsAffected 2 + // Columns: [Qty Name] + // {Qty:314 Name:bar} + // {Qty:42 Name:foo} + // OK +} + +func Example_testMem() { + db, err := sql.Open("ql-mem", "mem.db") + if err != nil { + return + } + + defer func() { + if err := db.Close(); err != nil { + return + } + + fmt.Println("OK") + }() + + tx, err := db.Begin() + if err != nil { + return + } + + if _, err := tx.Exec("CREATE TABLE t (Qty int, Name string);"); err != nil { + return + } + + result, err := tx.Exec(` + INSERT INTO t VALUES + ($1, $2), + ($3, $4), + ; + `, + 1042, "foo-mem", + 1314, "bar-mem", + ) + if err != nil { + return + } + + if err = tx.Commit(); err != nil { + return + } + + id, err := result.LastInsertId() + if err != nil { + return + } + + aff, err := result.RowsAffected() + if err != nil { + return + } + + fmt.Printf("LastInsertId %d, RowsAffected %d\n", id, aff) + + rows, err := db.Query("SELECT * FROM t;") + if err != nil { + return + } + + cols, err := rows.Columns() + if err != nil { + return + } + + fmt.Printf("Columns: %v\n", cols) + + var data struct { + Qty int + Name string + } + + for rows.Next() { + if err = rows.Scan(&data.Qty, &data.Name); err != nil { + rows.Close() + break + } + + fmt.Printf("%+v\n", data) + } + + if err = rows.Err(); err != nil { + return + } + + // Output: + // LastInsertId 2, RowsAffected 2 + // Columns: [Qty Name] + // {Qty:1314 Name:bar-mem} + // {Qty:1042 Name:foo-mem} + // OK +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/driver/driver.go b/Godeps/_workspace/src/github.com/cznic/ql/driver/driver.go new file mode 100644 index 000000000..557c70d82 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/driver/driver.go @@ -0,0 +1,61 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package driver registers a QL sql/driver named "ql" and a memory driver named "ql-mem". + +See also [0], [1] and [3]. + +Usage + +A skeleton program using ql/driver. + + package main + + import ( + "database/sql" + + _ "github.com/cznic/ql/driver" + ) + + func main() { + ... + // Disk file DB + db, err := sql.Open("ql", "ql.db") // [2] + // alternatively + db, err := sql.Open("ql", "file://ql.db") + + // and/or + + // RAM DB + mdb, err := sql.Open("ql-mem", "mem.db") + // alternatively + mdb, err := sql.Open("ql", "memory://mem.db") + if err != nil { + log.Fatal(err) + } + + // Use db/mdb here + ... + } + +This package exports nothing. + +Links + +Referenced from above: + + [0]: http://godoc.org/github.com/cznic/ql + [1]: http://golang.org/pkg/database/sql/ + [2]: http://golang.org/pkg/database/sql/#Open + [3]: http://golang.org/pkg/database/sql/driver +*/ +package driver + +import "github.com/cznic/ql" + +func init() { + ql.RegisterDriver() + ql.RegisterMemDriver() +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/errors.go b/Godeps/_workspace/src/github.com/cznic/ql/errors.go new file mode 100644 index 000000000..305414f4a --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/errors.go @@ -0,0 +1,18 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "errors" +) + +var ( + errBeginTransNoCtx = errors.New("BEGIN TRANSACTION: Must use R/W context, have nil") + errCommitNotInTransaction = errors.New("COMMIT: Not in transaction") + errDivByZero = errors.New("division by zero") + errIncompatibleDBFormat = errors.New("incompatible DB format") + errNoDataForHandle = errors.New("read: no data for handle") + errRollbackNotInTransaction = errors.New("ROLLBACK: Not in transaction") +) diff --git a/Godeps/_workspace/src/github.com/cznic/ql/etc.go b/Godeps/_workspace/src/github.com/cznic/ql/etc.go new file mode 100644 index 000000000..c2e1524af --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/etc.go @@ -0,0 +1,2805 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "bytes" + "fmt" + "io" + "math" + "math/big" + "strings" + "time" +) + +// QL types. +const ( + qBool = 0x62 // 'b' + qComplex64 = 0x63 // 'c' + qComplex128 = 0x64 // 'd' + qFloat32 = 0x66 // 'f' + qFloat64 = 0x67 // 'g', alias float + qInt8 = 0x69 // 'i' + qInt16 = 0x6a // 'j' + qInt32 = 0x6b // 'k' + qInt64 = 0x6c // 'l', alias int + qString = 0x73 // 's' + qUint8 = 0x75 // 'u', alias byte + qUint16 = 0x76 // 'v' + qUint32 = 0x77 // 'w' + qUint64 = 0x78 // 'x', alias uint + + qBigInt = 0x49 // 'I' + qBigRat = 0x52 // 'R' + qBlob = 0x42 // 'B' + qDuration = 0x44 // 'D' + qTime = 0x54 // 'T' +) + +var ( + type2Str = map[int]string{ + qBigInt: "bigint", + qBigRat: "bigrat", + qBlob: "blob", + qBool: "bool", + qComplex128: "complex128", + qComplex64: "complex64", + qDuration: "duration", + qFloat32: "float32", + qFloat64: "float64", + qInt16: "int16", + qInt32: "int32", + qInt64: "int64", + qInt8: "int8", + qString: "string", + qTime: "time", + qUint16: "uint16", + qUint32: "uint32", + qUint64: "uint64", + qUint8: "uint8", + } +) + +func typeStr(typ int) (r string) { + return type2Str[typ] +} + +func noEOF(err error) error { + if err == io.EOF { + err = nil + } + return err +} + +func runErr(err error) error { return fmt.Errorf("run time error: %s", err) } + +func invXOp(s, x interface{}) error { + return fmt.Errorf("invalid operation: %v[%v] (index of type %T)", s, x, x) +} + +func invSOp(s interface{}) error { + return fmt.Errorf("cannot slice %s (type %T)", s, s) +} + +func invNegX(x interface{}) error { + return fmt.Errorf("invalid string index %v (index must be non-negative)", x) +} + +func invNegLO(x interface{}) error { + return fmt.Errorf("invalid LIMIT or OFFSET value %v (must be non-negative)", x) +} + +func invSliceNegX(x interface{}) error { + return fmt.Errorf("invalid slice index %v (index must be non-negative)", x) +} + +func invBoundX(s string, x uint64) error { + return fmt.Errorf("invalid string index %d (out of bounds for %d-byte string)", x, len(s)) +} + +func invSliceBoundX(s string, x uint64) error { + return fmt.Errorf("invalid slice index %d (out of bounds for %d-byte string)", x, len(s)) +} + +func intExpr(x interface{}) (i int64, err error) { + switch x := x.(type) { + case idealInt: + if x < 0 { + return 0, invNegLO(x) + } + + return int64(x), nil + case idealRune: + if x < 0 { + return 0, invNegLO(x) + } + + return int64(x), nil + case idealUint: + if x < 0 { + return 0, invNegLO(x) + } + + return int64(x), nil + case int8: + if x < 0 { + return 0, invNegLO(x) + } + + return int64(x), nil + case int16: + if x < 0 { + return 0, invNegLO(x) + } + + return int64(x), nil + case int32: + if x < 0 { + return 0, invNegLO(x) + } + + return int64(x), nil + case int64: + if x < 0 { + return 0, invNegLO(x) + } + + return int64(x), nil + case uint8: + return int64(x), nil + case uint16: + return int64(x), nil + case uint32: + return int64(x), nil + case uint64: + return int64(x), nil + default: + return 0, fmt.Errorf("non-integer expression: %v (value of type %T)", x, x) + } +} + +func limOffExpr(x interface{}) (i uint64, err error) { + switch x := x.(type) { + case idealInt: + if x < 0 { + return 0, invNegLO(x) + } + + return uint64(x), nil + case idealRune: + if x < 0 { + return 0, invNegLO(x) + } + + return uint64(x), nil + case idealUint: + if x < 0 { + return 0, invNegLO(x) + } + + return uint64(x), nil + case int8: + if x < 0 { + return 0, invNegLO(x) + } + + return uint64(x), nil + case int16: + if x < 0 { + return 0, invNegLO(x) + } + + return uint64(x), nil + case int32: + if x < 0 { + return 0, invNegLO(x) + } + + return uint64(x), nil + case int64: + if x < 0 { + return 0, invNegLO(x) + } + + return uint64(x), nil + case uint8: + return uint64(x), nil + case uint16: + return uint64(x), nil + case uint32: + return uint64(x), nil + case uint64: + return uint64(x), nil + default: + return 0, fmt.Errorf("non-integer used in LIMIT or OFFSET: %v (value of type %T)", x, x) + } +} + +func indexExpr(s *string, x interface{}) (i uint64, err error) { + switch x := x.(type) { + case idealFloat: + if x < 0 { + return 0, invNegX(x) + } + + if s != nil && int(x) >= len(*s) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case idealInt: + if x < 0 { + return 0, invNegX(x) + } + + if s != nil && int64(x) >= int64(len(*s)) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case idealRune: + if x < 0 { + return 0, invNegX(x) + } + + if s != nil && int32(x) >= int32(len(*s)) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case idealUint: + if x < 0 { + return 0, invNegX(x) + } + + if s != nil && uint64(x) >= uint64(len(*s)) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case int8: + if x < 0 { + return 0, invNegX(x) + } + + if s != nil && int(x) >= len(*s) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case int16: + if x < 0 { + return 0, invNegX(x) + } + + if s != nil && int(x) >= len(*s) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case int32: + if x < 0 { + return 0, invNegX(x) + } + + if s != nil && int(x) >= len(*s) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case int64: + if x < 0 { + return 0, invNegX(x) + } + + if s != nil && x >= int64(len(*s)) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case uint8: + if s != nil && int(x) >= len(*s) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case uint16: + if s != nil && int(x) >= len(*s) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case uint32: + if s != nil && x >= uint32(len(*s)) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case uint64: + if s != nil && x >= uint64(len(*s)) { + return 0, invBoundX(*s, uint64(x)) + } + + return uint64(x), nil + default: + return 0, fmt.Errorf("non-integer string index %v (value of type %T)", x, x) + } +} + +func sliceExpr(s *string, x interface{}, mod int) (i uint64, err error) { + switch x := x.(type) { + case idealFloat: + if x < 0 { + return 0, invSliceNegX(x) + } + + if s != nil && int(x) >= len(*s)+mod { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case idealInt: + if x < 0 { + return 0, invSliceNegX(x) + } + + if s != nil && int64(x) >= int64(len(*s)+mod) { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case idealRune: + if x < 0 { + return 0, invSliceNegX(x) + } + + if s != nil && int32(x) >= int32(len(*s)+mod) { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case idealUint: + if x < 0 { + return 0, invSliceNegX(x) + } + + if s != nil && uint64(x) >= uint64(len(*s)+mod) { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case int8: + if x < 0 { + return 0, invSliceNegX(x) + } + + if s != nil && int(x) >= len(*s)+mod { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case int16: + if x < 0 { + return 0, invSliceNegX(x) + } + + if s != nil && int(x) >= len(*s)+mod { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case int32: + if x < 0 { + return 0, invSliceNegX(x) + } + + if s != nil && int(x) >= len(*s)+mod { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case int64: + if x < 0 { + return 0, invSliceNegX(x) + } + + if s != nil && x >= int64(len(*s)+mod) { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case uint8: + if s != nil && int(x) >= len(*s)+mod { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case uint16: + if s != nil && int(x) >= len(*s)+mod { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case uint32: + if s != nil && x >= uint32(len(*s)+mod) { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + case uint64: + if s != nil && x >= uint64(len(*s)+mod) { + return 0, invSliceBoundX(*s, uint64(x)) + } + + return uint64(x), nil + default: + return 0, fmt.Errorf("invalid slice index %s (type %T)", x, x) + } +} + +type iop int + +func (o iop) String() string { + switch i := int(o); i { + case andand: + return "&&" + case andnot: + return "&^" + case lsh: + return "<<" + case le: + return "<=" + case eq: + return "==" + case ge: + return ">=" + case neq: + return "!=" + case oror: + return "||" + case rsh: + return ">>" + default: + return string(i) + } +} + +func ideal(v interface{}) interface{} { + switch x := v.(type) { + case idealComplex: + return complex128(x) + case idealFloat: + return float64(x) + case idealInt: + return int64(x) + case idealRune: + return int64(x) + case idealUint: + return uint64(x) + default: + return v + } +} + +func eval(v expression, execCtx *execCtx, ctx map[interface{}]interface{}) (y interface{}) { + y, err := expand1(v.eval(execCtx, ctx)) + if err != nil { + panic(err) // panic ok here + } + return +} + +func eval2(a, b expression, execCtx *execCtx, ctx map[interface{}]interface{}) (x, y interface{}) { + return eval(a, execCtx, ctx), eval(b, execCtx, ctx) +} + +func invOp2(x, y interface{}, o int) (interface{}, error) { + return nil, fmt.Errorf("invalid operation: %v %v %v (mismatched types %T and %T)", x, iop(o), y, ideal(x), ideal(y)) +} + +func undOp(x interface{}, o int) (interface{}, error) { + return nil, fmt.Errorf("invalid operation: %v%v (operator %v not defined on %T)", iop(o), x, iop(o), x) +} + +func undOp2(x, y interface{}, o int) (interface{}, error) { + return nil, fmt.Errorf("invalid operation: %v %v %v (operator %v not defined on %T)", x, iop(o), y, iop(o), x) +} + +func invConv(val interface{}, typ int) (interface{}, error) { + return nil, fmt.Errorf("cannot convert %v (type %T) to type %s", val, val, typeStr(typ)) +} + +func truncConv(val interface{}) (interface{}, error) { + return nil, fmt.Errorf("constant %v truncated to integer", val) +} + +func convert(val interface{}, typ int) (v interface{}, err error) { //NTYPE + if val == nil { + return nil, nil + } + + switch typ { + case qBool: + switch x := val.(type) { + //case nil: + //case idealComplex: + //case idealFloat: + //case idealInt: + //case idealRune: + //case idealUint: + case bool: + return bool(x), nil + //case complex64: + //case complex128: + //case float32: + //case float64: + //case int8: + //case int16: + //case int32: + //case int64: + //case string: + //case uint8: + //case uint16: + //case uint32: + //case uint64: + default: + return invConv(val, typ) + } + case qComplex64: + switch x := val.(type) { + //case nil: + case idealComplex: + return complex64(x), nil + case idealFloat: + return complex(float32(x), 0), nil + case idealInt: + return complex(float32(x), 0), nil + case idealRune: + return complex(float32(x), 0), nil + case idealUint: + return complex(float32(x), 0), nil + //case bool: + case complex64: + return complex64(x), nil + case complex128: + return complex64(x), nil + //case float32: + //case float64: + //case int8: + //case int16: + //case int32: + //case int64: + //case string: + //case uint8: + //case uint16: + //case uint32: + //case uint64: + default: + return invConv(val, typ) + } + case qComplex128: + switch x := val.(type) { + //case nil: + case idealComplex: + return complex128(x), nil + case idealFloat: + return complex(float64(x), 0), nil + case idealInt: + return complex(float64(x), 0), nil + case idealRune: + return complex(float64(x), 0), nil + case idealUint: + return complex(float64(x), 0), nil + //case bool: + case complex64: + return complex128(x), nil + case complex128: + return complex128(x), nil + //case float32: + //case float64: + //case int8: + //case int16: + //case int32: + //case int64: + //case string: + //case uint8: + //case uint16: + //case uint32: + //case uint64: + default: + return invConv(val, typ) + } + case qFloat32: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + return float32(x), nil + case idealInt: + return float32(x), nil + case idealRune: + return float32(x), nil + case idealUint: + return float32(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return float32(x), nil + case float64: + return float32(x), nil + case int8: + return float32(x), nil + case int16: + return float32(x), nil + case int32: + return float32(x), nil + case int64: + return float32(x), nil + //case string: + case uint8: + return float32(x), nil + case uint16: + return float32(x), nil + case uint32: + return float32(x), nil + case uint64: + return float32(x), nil + case *big.Int: + v, _ := big.NewRat(1, 1).SetInt(x).Float64() + return float32(v), nil + case *big.Rat: + v, _ := x.Float64() + return float32(v), nil + case time.Duration: + return float32(x), nil + default: + return invConv(val, typ) + } + case qFloat64: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + return float64(x), nil + case idealInt: + return float64(x), nil + case idealRune: + return float64(x), nil + case idealUint: + return float64(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return float64(x), nil + case float64: + return float64(x), nil + case int8: + return float64(x), nil + case int16: + return float64(x), nil + case int32: + return float64(x), nil + case int64: + return float64(x), nil + //case string: + case uint8: + return float64(x), nil + case uint16: + return float64(x), nil + case uint32: + return float64(x), nil + case uint64: + return float64(x), nil + case *big.Int: + v, _ := big.NewRat(1, 1).SetInt(x).Float64() + return v, nil + case *big.Rat: + v, _ := x.Float64() + return v, nil + case time.Duration: + return float64(x), nil + default: + return invConv(val, typ) + } + case qInt8: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + if _, frac := math.Modf(float64(x)); frac != 0 { + return truncConv(x) + } + + return int8(x), nil + case idealInt: + return int8(x), nil + case idealRune: + return int8(x), nil + case idealUint: + return int8(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return int8(x), nil + case float64: + return int8(x), nil + case int8: + return int8(x), nil + case int16: + return int8(x), nil + case int32: + return int8(x), nil + case int64: + return int8(x), nil + //case string: + case uint8: + return int8(x), nil + case uint16: + return int8(x), nil + case uint32: + return int8(x), nil + case uint64: + return int8(x), nil + case *big.Int: + return int8(x.Int64()), nil + case time.Duration: + return int8(x), nil + default: + return invConv(val, typ) + } + case qInt16: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + if _, frac := math.Modf(float64(x)); frac != 0 { + return truncConv(x) + } + + return int16(x), nil + case idealInt: + return int16(x), nil + case idealRune: + return int16(x), nil + case idealUint: + return int16(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return int16(x), nil + case float64: + return int16(x), nil + case int8: + return int16(x), nil + case int16: + return int16(x), nil + case int32: + return int16(x), nil + case int64: + return int16(x), nil + //case string: + case uint8: + return int16(x), nil + case uint16: + return int16(x), nil + case uint32: + return int16(x), nil + case uint64: + return int16(x), nil + case *big.Int: + return int16(x.Int64()), nil + case time.Duration: + return int16(x), nil + default: + return invConv(val, typ) + } + case qInt32: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + if _, frac := math.Modf(float64(x)); frac != 0 { + return truncConv(x) + } + + return int32(x), nil + case idealInt: + return int32(x), nil + case idealRune: + return int32(x), nil + case idealUint: + return int32(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return int32(x), nil + case float64: + return int32(x), nil + case int8: + return int32(x), nil + case int16: + return int32(x), nil + case int32: + return int32(x), nil + case int64: + return int32(x), nil + //case string: + case uint8: + return int32(x), nil + case uint16: + return int32(x), nil + case uint32: + return int32(x), nil + case uint64: + return int32(x), nil + case *big.Int: + return int32(x.Int64()), nil + case time.Duration: + return int32(x), nil + default: + return invConv(val, typ) + } + case qInt64: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + if _, frac := math.Modf(float64(x)); frac != 0 { + return truncConv(x) + } + + return int64(x), nil + case idealInt: + return int64(x), nil + case idealRune: + return int64(x), nil + case idealUint: + return int64(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return int64(x), nil + case float64: + return int64(x), nil + case int8: + return int64(x), nil + case int16: + return int64(x), nil + case int32: + return int64(x), nil + case int64: + return int64(x), nil + //case string: + case uint8: + return int64(x), nil + case uint16: + return int64(x), nil + case uint32: + return int64(x), nil + case uint64: + return int64(x), nil + case *big.Int: + return x.Int64(), nil + case time.Duration: + return int64(x), nil + default: + return invConv(val, typ) + } + case qString: + switch x := val.(type) { + //case nil: + //case idealComplex: + //case idealFloat: + case idealInt: + return string(x), nil + case idealRune: + return string(x), nil + case idealUint: + return string(x), nil + //case bool: + //case complex64: + //case complex128: + //case float32: + //case float64: + case int8: + return string(x), nil + case int16: + return string(x), nil + case int32: + return string(x), nil + case int64: + return string(x), nil + case string: + return string(x), nil + case uint8: + return string(x), nil + case uint16: + return string(x), nil + case uint32: + return string(x), nil + case uint64: + return string(x), nil + case []byte: + return string(x), nil + case *big.Int: + return x.String(), nil + case time.Time: + return x.String(), nil + case time.Duration: + return x.String(), nil + default: + return invConv(val, typ) + } + case qUint8: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + if _, frac := math.Modf(float64(x)); frac != 0 { + return truncConv(x) + } + + return uint8(x), nil + case idealInt: + return uint8(x), nil + case idealRune: + return uint8(x), nil + case idealUint: + return uint8(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return uint8(x), nil + case float64: + return uint8(x), nil + case int8: + return uint8(x), nil + case int16: + return uint8(x), nil + case int32: + return uint8(x), nil + case int64: + return uint8(x), nil + //case string: + case uint8: + return uint8(x), nil + case uint16: + return uint8(x), nil + case uint32: + return uint8(x), nil + case uint64: + return uint8(x), nil + case *big.Int: + return uint8(x.Int64()), nil + case time.Duration: + return uint8(x), nil + default: + return invConv(val, typ) + } + case qUint16: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + if _, frac := math.Modf(float64(x)); frac != 0 { + return truncConv(x) + } + + return uint16(x), nil + case idealInt: + return uint16(x), nil + case idealRune: + return uint16(x), nil + case idealUint: + return uint16(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return uint16(x), nil + case float64: + return uint16(x), nil + case int8: + return uint16(x), nil + case int16: + return uint16(x), nil + case int32: + return uint16(x), nil + case int64: + return uint16(x), nil + //case string: + case uint8: + return uint16(x), nil + case uint16: + return uint16(x), nil + case uint32: + return uint16(x), nil + case uint64: + return uint16(x), nil + case *big.Int: + return uint16(x.Int64()), nil + case time.Duration: + return uint16(x), nil + default: + return invConv(val, typ) + } + case qUint32: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + if _, frac := math.Modf(float64(x)); frac != 0 { + return truncConv(x) + } + + return uint32(x), nil + case idealInt: + return uint32(x), nil + case idealRune: + return uint32(x), nil + case idealUint: + return uint32(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return uint32(x), nil + case float64: + return uint32(x), nil + case int8: + return uint32(x), nil + case int16: + return uint32(x), nil + case int32: + return uint32(x), nil + case int64: + return uint32(x), nil + //case string: + case uint8: + return uint32(x), nil + case uint16: + return uint32(x), nil + case uint32: + return uint32(x), nil + case uint64: + return uint32(x), nil + case *big.Int: + return uint32(x.Int64()), nil + case time.Duration: + return uint32(x), nil + default: + return invConv(val, typ) + } + case qUint64: + switch x := val.(type) { + //case nil: + //case idealComplex: + case idealFloat: + if _, frac := math.Modf(float64(x)); frac != 0 { + return truncConv(x) + } + + return uint64(x), nil + case idealInt: + return uint64(x), nil + case idealRune: + return uint64(x), nil + case idealUint: + return uint64(x), nil + //case bool: + //case complex64: + //case complex128: + case float32: + return uint64(x), nil + case float64: + return uint64(x), nil + case int8: + return uint64(x), nil + case int16: + return uint64(x), nil + case int32: + return uint64(x), nil + case int64: + return uint64(x), nil + //case string: + case uint8: + return uint64(x), nil + case uint16: + return uint64(x), nil + case uint32: + return uint64(x), nil + case uint64: + return uint64(x), nil + case *big.Int: + return x.Uint64(), nil + case time.Duration: + return uint64(x), nil + default: + return invConv(val, typ) + } + case qBlob: + switch x := val.(type) { + case string: + return []byte(x), nil + case []byte: + return x, nil + default: + return invConv(val, typ) + } + case qBigInt: + switch x := val.(type) { + // case blob + // case bool + //case idealComplex: + case idealFloat: + if _, frac := math.Modf(float64(x)); frac != 0 { + return truncConv(x) + } + + rr := big.NewRat(1, 1).SetFloat64(float64(x)) + ii := big.NewInt(0).Set(rr.Num()) + ii.Quo(ii, rr.Denom()) + return ii, nil + case idealInt: + return big.NewInt(0).SetInt64(int64(x)), nil + case idealRune: + return big.NewInt(0).SetInt64(int64(x)), nil + case idealUint: + return big.NewInt(0).SetUint64(uint64(x)), nil + //case complex64 + //case complex128 + case float32: + rr := big.NewRat(1, 1).SetFloat64(float64(x)) + ii := big.NewInt(0).Set(rr.Num()) + ii.Quo(ii, rr.Denom()) + return ii, nil + case float64: + rr := big.NewRat(1, 1).SetFloat64(float64(x)) + ii := big.NewInt(0).Set(rr.Num()) + ii.Quo(ii, rr.Denom()) + return ii, nil + case int8: + return big.NewInt(0).SetInt64(int64(x)), nil + case int16: + return big.NewInt(0).SetInt64(int64(x)), nil + case int32: + return big.NewInt(0).SetInt64(int64(x)), nil + case int64: + return big.NewInt(0).SetInt64(x), nil + case string: + y := big.NewInt(0) + if _, ok := y.SetString(x, 0); !ok { + return invConv(val, typ) + } + + return y, nil + case uint8: + return big.NewInt(0).SetUint64(uint64(x)), nil + case uint16: + return big.NewInt(0).SetUint64(uint64(x)), nil + case uint32: + return big.NewInt(0).SetUint64(uint64(x)), nil + case uint64: + return big.NewInt(0).SetUint64(x), nil + case *big.Int: + return x, nil + case *big.Rat: + ii := big.NewInt(0).Set(x.Num()) + ii.Div(ii, x.Denom()) + return ii, nil + default: + return invConv(val, typ) + } + case qBigRat: + switch x := val.(type) { + // case blob + // case bool + //case idealComplex: + case idealFloat: + return big.NewRat(1, 1).SetFloat64(float64(x)), nil + case idealInt: + return big.NewRat(1, 1).SetInt64(int64(x)), nil + case idealRune: + return big.NewRat(1, 1).SetInt64(int64(x)), nil + case idealUint: + return big.NewRat(1, 1).SetInt(big.NewInt(0).SetUint64(uint64(x))), nil + //case complex64 + //case complex128 + case float32: + return big.NewRat(1, 1).SetFloat64(float64(x)), nil + case float64: + return big.NewRat(1, 1).SetFloat64(x), nil + case int8: + return big.NewRat(1, 1).SetInt64(int64(x)), nil + case int16: + return big.NewRat(1, 1).SetInt64(int64(x)), nil + case int32: + return big.NewRat(1, 1).SetInt64(int64(x)), nil + case int64: + return big.NewRat(1, 1).SetInt64(x), nil + case string: + y := big.NewRat(1, 1) + if _, ok := y.SetString(x); !ok { + return invConv(val, typ) + } + + return y, nil + case uint8: + return big.NewRat(1, 1).SetInt64(int64(x)), nil + case uint16: + return big.NewRat(1, 1).SetInt64(int64(x)), nil + case uint32: + return big.NewRat(1, 1).SetInt64(int64(x)), nil + case uint64: + return big.NewRat(1, 1).SetInt(big.NewInt(0).SetUint64(x)), nil + case *big.Int: + return big.NewRat(1, 1).SetInt(x), nil + case *big.Rat: + return x, nil + default: + return invConv(val, typ) + } + case qDuration: + switch x := val.(type) { + // case blob + // case bool + //case idealComplex: + case idealFloat: + return time.Duration(x), nil + case idealInt: + return time.Duration(x), nil + case idealRune: + return time.Duration(x), nil + case idealUint: + return time.Duration(x), nil + //case complex64 + //case complex128 + case float32: + return time.Duration(x), nil + case float64: + return time.Duration(x), nil + case int8: + return time.Duration(x), nil + case int16: + return time.Duration(x), nil + case int32: + return time.Duration(x), nil + case int64: + return time.Duration(x), nil + case string: + return time.ParseDuration(x) + case uint8: + return time.Duration(x), nil + case uint16: + return time.Duration(x), nil + case uint32: + return time.Duration(x), nil + case uint64: + return time.Duration(x), nil + case *big.Int: + return time.Duration(x.Int64()), nil + case *big.Rat: + f, _ := x.Float64() + return time.Duration(f), nil + case time.Duration: + return x, nil + default: + return invConv(val, typ) + } + case qTime: + switch x := val.(type) { + // case blob + // case bool + //case idealComplex: + //case idealFloat: + //case idealInt: + //case idealRune: + //case idealUint: + //case complex64 + //case complex128 + //case float32: + //case float64: + //case int8: + //case int16: + //case int32: + //case int64: + //case string: + //case uint8: + //case uint16: + //case uint32: + //case uint64: + //case *big.Int: + //case *big.Rat: + //case time.Duration: + case time.Time: + return x, nil + default: + return invConv(val, typ) + } + default: + panic("internal error 006") + } +} + +func invShiftRHS(lhs, rhs interface{}) (interface{}, error) { + return nil, fmt.Errorf("invalid operation: %v << %v (shift count type %T, must be unsigned integer)", lhs, rhs, rhs) +} + +func invTruncInt(v interface{}) error { + return fmt.Errorf("constant %v truncated to integer", v) +} + +func overflow(v interface{}, typ int) error { + return fmt.Errorf("constant %v overflows %s", v, typeStr(typ)) +} + +func typeCheck1(val interface{}, c *col) (interface{}, error) { + rec := []interface{}{val} + c = c.clone() + c.index = 0 + if err := typeCheck(rec, []*col{c}); err != nil { + return nil, err + } + + return rec[0], nil +} + +func typeCheck(rec []interface{}, cols []*col) (err error) { + for _, c := range cols { + i := c.index + if v := rec[i]; !c.typeCheck(v) { + switch v.(type) { + case idealComplex: + y := complex128(v.(idealComplex)) + switch c.typ { + case qBool: + case qComplex64: + rec[i] = complex64(y) + continue + case qComplex128: + rec[i] = complex128(y) + continue + case qFloat32, qFloat64, qInt8, qInt16, qInt32, qInt64, qUint8, qUint16, qUint32, qUint64: + return fmt.Errorf("constant %v truncated to real", y) + } + case idealFloat: + y := float64(v.(idealFloat)) + switch c.typ { + case qBool: + case qComplex64: + rec[i] = complex(float32(y), 0) + continue + case qComplex128: + rec[i] = complex(float64(y), 0) + continue + case qFloat32: + rec[i] = float32(y) + continue + case qFloat64: + rec[i] = float64(y) + continue + case qInt8: + if math.Floor(y) != y { + return invTruncInt(y) + } + + if y < math.MinInt8 || y > math.MaxInt8 { + return overflow(y, c.typ) + } + + rec[i] = int8(y) + continue + case qInt16: + if math.Floor(y) != y { + return invTruncInt(y) + } + + if y < math.MinInt16 || y > math.MaxInt16 { + return overflow(y, c.typ) + } + + rec[i] = int16(y) + continue + case qInt32: + if math.Floor(y) != y { + return invTruncInt(y) + } + + if y < math.MinInt32 || y > math.MaxInt32 { + return overflow(y, c.typ) + } + + rec[i] = int32(y) + continue + case qInt64: + if math.Floor(y) != y { + return invTruncInt(y) + } + + if y < math.MinInt64 || y > math.MaxInt64 { + return overflow(y, c.typ) + } + + rec[i] = int64(y) + continue + case qString: + case qUint8: + if math.Floor(y) != y { + return invTruncInt(y) + } + + if y < 0 || y > math.MaxUint8 { + return overflow(y, c.typ) + } + + rec[i] = uint8(y) + continue + case qUint16: + if math.Floor(y) != y { + return invTruncInt(y) + } + + if y < 0 || y > math.MaxUint16 { + return overflow(y, c.typ) + } + + rec[i] = uint16(y) + continue + case qUint32: + if math.Floor(y) != y { + return invTruncInt(y) + } + + if y < 0 || y > math.MaxUint32 { + return overflow(y, c.typ) + } + + rec[i] = uint32(y) + continue + case qUint64: + if math.Floor(y) != y { + return invTruncInt(y) + } + + if y < 0 || y > math.MaxUint64 { + return overflow(y, c.typ) + } + + rec[i] = uint64(y) + continue + case qBigInt: + if math.Floor(y) != y { + return invTruncInt(y) + } + + rr := big.NewRat(1, 1).SetFloat64(y) + ii := big.NewInt(0) + ii.Set(rr.Num()) + ii.Quo(ii, rr.Denom()) + rec[i] = ii + continue + case qBigRat: + rec[i] = big.NewRat(1, 1).SetFloat64(y) + continue + } + case idealInt: + y := int64(v.(idealInt)) + switch c.typ { + case qBool: + case qComplex64: + rec[i] = complex(float32(y), 0) + continue + case qComplex128: + rec[i] = complex(float64(y), 0) + continue + case qFloat32: + rec[i] = float32(y) + continue + case qFloat64: + rec[i] = float64(y) + continue + case qInt8: + if y < math.MinInt8 || y > math.MaxInt8 { + return overflow(y, c.typ) + } + + rec[i] = int8(y) + continue + case qInt16: + if y < math.MinInt16 || y > math.MaxInt16 { + return overflow(y, c.typ) + } + + rec[i] = int16(y) + continue + case qInt32: + if y < math.MinInt32 || y > math.MaxInt32 { + return overflow(y, c.typ) + } + + rec[i] = int32(y) + continue + case qInt64: + if y < math.MinInt64 || y > math.MaxInt64 { + return overflow(y, c.typ) + } + + rec[i] = int64(y) + continue + case qString: + case qUint8: + if y < 0 || y > math.MaxUint8 { + return overflow(y, c.typ) + } + + rec[i] = uint8(y) + continue + case qUint16: + if y < 0 || y > math.MaxUint16 { + return overflow(y, c.typ) + } + + rec[i] = uint16(y) + continue + case qUint32: + if y < 0 || y > math.MaxUint32 { + return overflow(y, c.typ) + } + + rec[i] = uint32(y) + continue + case qUint64: + if y < 0 { + return overflow(y, c.typ) + } + + rec[i] = uint64(y) + continue + case qBigInt: + rec[i] = big.NewInt(y) + continue + case qBigRat: + rec[i] = big.NewRat(1, 1).SetInt64(y) + continue + } + case idealRune: + y := int64(v.(idealRune)) + switch c.typ { + case qBool: + case qComplex64: + rec[i] = complex(float32(y), 0) + continue + case qComplex128: + rec[i] = complex(float64(y), 0) + continue + case qFloat32: + rec[i] = float32(y) + continue + case qFloat64: + rec[i] = float64(y) + continue + case qInt8: + if y < math.MinInt8 || y > math.MaxInt8 { + return overflow(y, c.typ) + } + + rec[i] = int8(y) + continue + case qInt16: + if y < math.MinInt16 || y > math.MaxInt16 { + return overflow(y, c.typ) + } + + rec[i] = int16(y) + continue + case qInt32: + if y < math.MinInt32 || y > math.MaxInt32 { + return overflow(y, c.typ) + } + + rec[i] = int32(y) + continue + case qInt64: + if y < math.MinInt64 || y > math.MaxInt64 { + return overflow(y, c.typ) + } + + rec[i] = int64(y) + continue + case qString: + case qUint8: + if y < 0 || y > math.MaxUint8 { + return overflow(y, c.typ) + } + + rec[i] = uint8(y) + continue + case qUint16: + if y < 0 || y > math.MaxUint16 { + return overflow(y, c.typ) + } + + rec[i] = uint16(y) + continue + case qUint32: + if y < 0 { + return overflow(y, c.typ) + } + + rec[i] = uint32(y) + continue + case qUint64: + if y < 0 { + return overflow(y, c.typ) + } + + rec[i] = uint64(y) + continue + case qBigInt: + rec[i] = big.NewInt(y) + continue + case qBigRat: + rec[i] = big.NewRat(1, 1).SetInt64(y) + continue + } + case idealUint: + y := uint64(v.(idealUint)) + switch c.typ { + case qBool: + case qComplex64: + rec[i] = complex(float32(y), 0) + continue + case qComplex128: + rec[i] = complex(float64(y), 0) + continue + case qFloat32: + rec[i] = float32(y) + continue + case qFloat64: + rec[i] = float64(y) + continue + case qInt8: + if y > math.MaxInt8 { + return overflow(y, c.typ) + } + + rec[i] = int8(y) + continue + case qInt16: + if y > math.MaxInt16 { + return overflow(y, c.typ) + } + + rec[i] = int16(y) + continue + case qInt32: + if y > math.MaxInt32 { + return overflow(y, c.typ) + } + + rec[i] = int32(y) + continue + case qInt64: + if y > math.MaxInt64 { + return overflow(y, c.typ) + } + + rec[i] = int64(y) + continue + case qString: + rec[i] = string(y) + continue + case qUint8: + if y > math.MaxUint8 { + return overflow(y, c.typ) + } + + rec[i] = uint8(y) + continue + case qUint16: + if y > math.MaxUint16 { + return overflow(y, c.typ) + } + + rec[i] = uint16(y) + continue + case qUint32: + if y > math.MaxUint32 { + return overflow(y, c.typ) + } + + rec[i] = uint32(y) + continue + case qUint64: + rec[i] = uint64(y) + continue + case qBigInt: + rec[i] = big.NewInt(0).SetUint64(y) + continue + case qBigRat: + ii := big.NewInt(0).SetUint64(y) + rec[i] = big.NewRat(1, 1).SetInt(ii) + continue + } + } + return fmt.Errorf("cannot use %v (type %T) in assignment to, or comparison with, column %s (type %s)", v, ideal(v), c.name, typeStr(c.typ)) + } + } + return +} + +//TODO collate1 should return errors instead of panicing +func collate1(a, b interface{}) int { + switch x := a.(type) { + case nil: + if b != nil { + return -1 + } + + return 0 + case bool: + switch y := b.(type) { + case nil: + return 1 + case bool: + if !x && y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + default: + // Make bool collate before anything except nil and + // other bool for index seeking first non NULL value. + return -1 + } + case idealComplex: + switch y := b.(type) { + case nil: + return 1 + case idealComplex: + if x == y { + return 0 + } + + if real(x) < real(y) { + return -1 + } + + if real(x) > real(y) { + return 1 + } + + if imag(x) < imag(y) { + return -1 + } + + return 1 + case complex64: + { + x, y := complex64(x), complex64(y) + if x == y { + return 0 + } + + if real(x) < real(y) { + return -1 + } + + if real(x) > real(y) { + return 1 + } + + if imag(x) < imag(y) { + return -1 + } + + return 1 + } + case complex128: + { + x := complex128(x) + if x == y { + return 0 + } + + if real(x) < real(y) { + return -1 + } + + if real(x) > real(y) { + return 1 + } + + if imag(x) < imag(y) { + return -1 + } + + return 1 + } + default: + panic("internal error 012") + } + case idealUint: + switch y := b.(type) { + case nil: + return 1 + case idealUint: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case uint8: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case uint16: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case uint32: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case uint64: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case uint: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 013") + } + case idealRune: + switch y := b.(type) { + case nil: + return 1 + case idealRune: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case int8: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case int16: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case int32: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case int64: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case int: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 014") + } + case idealInt: + switch y := b.(type) { + case nil: + return 1 + case idealInt: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case int8: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case int16: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case int32: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case int64: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case int: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 015") + } + case idealFloat: + switch y := b.(type) { + case nil: + return 1 + case idealFloat: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case float32: + { + x, y := float64(x), float64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case float64: + { + x, y := float64(x), float64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 016") + } + case complex64: + switch y := b.(type) { + case nil: + return 1 + case complex64: + if x == y { + return 0 + } + + if real(x) < real(y) { + return -1 + } + + if real(x) > real(y) { + return 1 + } + + if imag(x) < imag(y) { + return -1 + } + + return 1 + case idealComplex: + { + x, y := complex64(x), complex64(y) + if x == y { + return 0 + } + + if real(x) < real(y) { + return -1 + } + + if real(x) > real(y) { + return 1 + } + + if imag(x) < imag(y) { + return -1 + } + + return 1 + } + default: + panic("internal error 017") + } + case complex128: + switch y := b.(type) { + case nil: + return 1 + case complex128: + if x == y { + return 0 + } + + if real(x) < real(y) { + return -1 + } + + if real(x) > real(y) { + return 1 + } + + if imag(x) < imag(y) { + return -1 + } + + return 1 + case idealComplex: + { + x, y := complex128(x), complex128(y) + if x == y { + return 0 + } + + if real(x) < real(y) { + return -1 + } + + if real(x) > real(y) { + return 1 + } + + if imag(x) < imag(y) { + return -1 + } + + return 1 + } + default: + panic("internal error 018") + } + case float32: + switch y := b.(type) { + case nil: + return 1 + case float32: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealFloat: + { + x, y := float32(x), float32(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 019") + } + case float64: + switch y := b.(type) { + case nil: + return 1 + case float64: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealFloat: + { + x, y := float64(x), float64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 020") + } + case int8: + switch y := b.(type) { + case nil: + return 1 + case int8: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealInt: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 021") + } + case int16: + switch y := b.(type) { + case nil: + return 1 + case int16: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealInt: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 022") + } + case int32: + switch y := b.(type) { + case nil: + return 1 + case int32: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealInt: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 023") + } + case int64: + switch y := b.(type) { + case nil: + return 1 + case int64: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealInt: + { + x, y := int64(x), int64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 024") + } + case uint8: + switch y := b.(type) { + case nil: + return 1 + case uint8: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealInt: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case idealUint: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 025") + } + case uint16: + switch y := b.(type) { + case nil: + return 1 + case uint16: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealInt: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case idealUint: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 026") + } + case uint32: + switch y := b.(type) { + case nil: + return 1 + case uint32: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealInt: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case idealUint: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 027") + } + case uint64: + switch y := b.(type) { + case nil: + return 1 + case uint64: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + case idealInt: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + case idealUint: + { + x, y := uint64(x), uint64(y) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + } + default: + panic("internal error 028") + } + case string: + switch y := b.(type) { + case nil: + return 1 + case string: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + default: + panic("internal error 029") + } + case []byte: + switch y := b.(type) { + case nil: + return 1 + case []byte: + return bytes.Compare(x, y) + default: + panic("internal error 030") + } + case *big.Int: + switch y := b.(type) { + case nil: + return 1 + case *big.Int: + return x.Cmp(y) + case idealInt: + { + y := big.NewInt(int64(y)) + return x.Cmp(y) + } + case idealUint: + { + u := big.NewInt(0) + u.SetUint64(uint64(y)) + return x.Cmp(u) + } + default: + panic("internal error 031") + } + case *big.Rat: + switch y := b.(type) { + case nil: + return 1 + case *big.Rat: + return x.Cmp(y) + case idealInt: + { + y := big.NewRat(int64(y), 1) + return x.Cmp(y) + } + case idealUint: + { + u := big.NewInt(0) + u.SetUint64(uint64(y)) + var y big.Rat + y.SetInt(u) + return x.Cmp(&y) + } + default: + panic("internal error 032") + } + case time.Time: + switch y := b.(type) { + case nil: + return 1 + case time.Time: + if x.Before(y) { + return -1 + } + + if x.Equal(y) { + return 0 + } + + return 1 + default: + panic("internal error 033") + } + case time.Duration: + switch y := b.(type) { + case nil: + return 1 + case time.Duration: + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + default: + panic("internal error 034") + } + case chunk: + switch y := b.(type) { + case nil: + return 1 + case chunk: + a, err := x.expand() + if err != nil { + panic(err) + } + + b, err := y.expand() + if err != nil { + panic(err) + } + + return collate1(a, b) + default: + panic("internal error 035") + } + default: + //dbg("%T(%v) %T(%v)", a, a, b, b) + panic("internal error 036") + } +} + +//TODO collate should return errors from collate1 +func collate(x, y []interface{}) (r int) { + //defer func() { dbg("%v %v -> %v", x, y, r) }() + nx, ny := len(x), len(y) + + switch { + case nx == 0 && ny != 0: + return -1 + case nx == 0 && ny == 0: + return 0 + case nx != 0 && ny == 0: + return 1 + } + + r = 1 + if nx > ny { + x, y, r = y, x, -r + } + + for i, xi := range x { + if c := collate1(xi, y[i]); c != 0 { + return c * r + } + } + + if nx == ny { + return 0 + } + + return -r +} + +var collators = map[bool]func(a, b []interface{}) int{false: collateDesc, true: collate} + +func collateDesc(a, b []interface{}) int { + return -collate(a, b) +} + +func isOrderedType(v interface{}) (y interface{}, r bool, err error) { + //dbg("====") + //dbg("%T(%v)", v, v) + //defer func() { dbg("%T(%v)", y, y) }() + switch x := v.(type) { + case idealFloat, idealInt, idealRune, idealUint, + float32, float64, + int8, int16, int32, int64, + uint8, uint16, uint32, uint64, + string: + return v, true, nil + case *big.Int, *big.Rat, time.Time, time.Duration: + return x, true, nil + case chunk: + if y, err = x.expand(); err != nil { + return + } + + return isOrderedType(y) + } + + return v, false, nil +} + +var isSystemName = map[string]bool{ + "__Column": true, + "__Column2": true, + "__Index": true, + "__Index2": true, + "__Index2_Column": true, + "__Index2_Expr": true, + "__Table": true, +} + +func qualifier(s string) string { + if pos := strings.IndexByte(s, '.'); pos >= 0 { + s = s[:pos] + } + return s +} + +func mustQualifier(s string) string { + q := qualifier(s) + if q == s { + panic("internal error 068") + } + + return q +} + +func selector(s string) string { + if pos := strings.IndexByte(s, '.'); pos >= 0 { + s = s[pos+1:] + } + return s +} + +func mustSelector(s string) string { + q := selector(s) + if q == s { + panic("internal error 053") + } + + return q +} + +func qnames(l []string) []string { + r := make([]string, len(l)) + for i, v := range l { + r[i] = fmt.Sprintf("%q", v) + } + return r +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/expr.go b/Godeps/_workspace/src/github.com/cznic/ql/expr.go new file mode 100644 index 000000000..3e6954fbe --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/expr.go @@ -0,0 +1,4023 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found pIn the LICENSE file. + +package ql + +import ( + "fmt" + "math/big" + "regexp" + "strings" + "time" +) + +var ( + _ expression = (*binaryOperation)(nil) + _ expression = (*call)(nil) + _ expression = (*conversion)(nil) + _ expression = (*ident)(nil) + _ expression = (*indexOp)(nil) + _ expression = (*isNull)(nil) + _ expression = (*pIn)(nil) + _ expression = (*pLike)(nil) + _ expression = (*parameter)(nil) + _ expression = (*pexpr)(nil) + _ expression = (*slice)(nil) + _ expression = (*unaryOperation)(nil) + _ expression = value{} +) + +type expression interface { + clone(arg []interface{}, unqualify ...string) (expression, error) + eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) + isStatic() bool + String() string +} + +func cloneExpressionList(arg []interface{}, list []expression, unqualify ...string) ([]expression, error) { + r := make([]expression, len(list)) + var err error + for i, v := range list { + if r[i], err = v.clone(arg, unqualify...); err != nil { + return nil, err + } + } + return r, nil +} + +func isConstValue(v interface{}) interface{} { + switch x := v.(type) { + case value: + return x.val + case + idealComplex, + idealFloat, + idealInt, + idealRune, + idealUint: + return v + default: + return nil + } +} + +func isColumnExpression(v expression) (bool, string) { + x, ok := v.(*ident) + if ok { + return true, x.s + } + + c, ok := v.(*call) + if !ok || c.f != "id" || len(c.arg) != 0 { + return false, "" + } + + return true, "id()" +} + +func mentionedColumns0(e expression, q, nq bool, m map[string]struct{}) { + switch x := e.(type) { + case parameter, + value: + // nop + case *binaryOperation: + mentionedColumns0(x.l, q, nq, m) + mentionedColumns0(x.r, q, nq, m) + case *call: + if x.f != "id" { + for _, e := range x.arg { + mentionedColumns0(e, q, nq, m) + } + } + case *conversion: + mentionedColumns0(x.val, q, nq, m) + case *ident: + if q && x.isQualified() { + m[x.s] = struct{}{} + } + if nq && !x.isQualified() { + m[x.s] = struct{}{} + } + case *indexOp: + mentionedColumns0(x.expr, q, nq, m) + mentionedColumns0(x.x, q, nq, m) + case *isNull: + mentionedColumns0(x.expr, q, nq, m) + case *pexpr: + mentionedColumns0(x.expr, q, nq, m) + case *pIn: + mentionedColumns0(x.expr, q, nq, m) + for _, e := range x.list { + mentionedColumns0(e, q, nq, m) + } + case *pLike: + mentionedColumns0(x.expr, q, nq, m) + mentionedColumns0(x.pattern, q, nq, m) + case *slice: + mentionedColumns0(x.expr, q, nq, m) + if y := x.lo; y != nil { + mentionedColumns0(*y, q, nq, m) + } + if y := x.hi; y != nil { + mentionedColumns0(*y, q, nq, m) + } + case *unaryOperation: + mentionedColumns0(x.v, q, nq, m) + default: + panic("internal error 052") + } +} + +func mentionedColumns(e expression) map[string]struct{} { + m := map[string]struct{}{} + mentionedColumns0(e, false, true, m) + return m +} + +func mentionedQColumns(e expression) map[string]struct{} { + m := map[string]struct{}{} + mentionedColumns0(e, true, false, m) + return m +} + +func staticExpr(e expression) (expression, error) { + if e.isStatic() { + v, err := e.eval(nil, nil) + if err != nil { + return nil, err + } + + if v == nil { + return value{nil}, nil + } + + return value{v}, nil + } + + return e, nil +} + +type ( + idealComplex complex128 + idealFloat float64 + idealInt int64 + idealRune int32 + idealUint uint64 +) + +type exprTab struct { + expr expression + table string +} + +type pexpr struct { + expr expression +} + +func (p *pexpr) clone(arg []interface{}, unqualify ...string) (expression, error) { + expr, err := p.expr.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + return &pexpr{expr: expr}, nil +} + +func (p *pexpr) isStatic() bool { return p.expr.isStatic() } + +func (p *pexpr) String() string { + return fmt.Sprintf("(%s)", p.expr) +} + +func (p *pexpr) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + return p.expr.eval(execCtx, ctx) +} + +//DONE newBetween +//LATER like newBetween, check all others have and use new* + +func newBetween(expr, lo, hi interface{}, not bool) (expression, error) { + e, err := staticExpr(expr.(expression)) + if err != nil { + return nil, err + } + + l, err := staticExpr(lo.(expression)) + if err != nil { + return nil, err + } + + h, err := staticExpr(hi.(expression)) + if err != nil { + return nil, err + } + + var a, b expression + op := andand + switch { + case not: // e < l || e > h + op = oror + if a, err = newBinaryOperation('<', e, l); err != nil { + return nil, err + } + + if b, err = newBinaryOperation('>', e, h); err != nil { + return nil, err + } + default: // e >= l && e <= h + if a, err = newBinaryOperation(ge, e, l); err != nil { + return nil, err + } + + if b, err = newBinaryOperation(le, e, h); err != nil { + return nil, err + } + } + + if a, err = staticExpr(a); err != nil { + return nil, err + } + + if b, err = staticExpr(b); err != nil { + return nil, err + } + + ret, err := newBinaryOperation(op, a, b) + if err != nil { + return nil, err + } + + return staticExpr(ret) +} + +type pLike struct { + expr expression + pattern expression + re *regexp.Regexp + sexpr *string +} + +func (p *pLike) clone(arg []interface{}, unqualify ...string) (expression, error) { + expr, err := p.expr.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + pattern, err := p.pattern.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + return &pLike{ + expr: expr, + pattern: pattern, + re: p.re, + sexpr: p.sexpr, + }, nil +} + +func (p *pLike) isStatic() bool { return p.expr.isStatic() && p.pattern.isStatic() } +func (p *pLike) String() string { return fmt.Sprintf("%s LIKE %s", p.expr, p.pattern) } + +func (p *pLike) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + var sexpr string + var ok bool + switch { + case p.sexpr != nil: + sexpr = *p.sexpr + default: + expr, err := expand1(p.expr.eval(execCtx, ctx)) + if err != nil { + return nil, err + } + + if expr == nil { + return nil, nil + } + + sexpr, ok = expr.(string) + if !ok { + return nil, fmt.Errorf("non-string expression in LIKE: %v (value of type %T)", expr, expr) + } + + if p.expr.isStatic() { + p.sexpr = new(string) + *p.sexpr = sexpr + } + } + + re := p.re + if re == nil { + pattern, err := expand1(p.pattern.eval(execCtx, ctx)) + if err != nil { + return nil, err + } + + if pattern == nil { + return nil, nil + } + + spattern, ok := pattern.(string) + if !ok { + return nil, fmt.Errorf("non-string pattern in LIKE: %v (value of type %T)", pattern, pattern) + } + + if re, err = regexp.Compile(spattern); err != nil { + return nil, err + } + + if p.pattern.isStatic() { + p.re = re + } + } + + return re.MatchString(sexpr), nil +} + +type binaryOperation struct { + op int + l, r expression +} + +func newBinaryOperation0(op int, x, y interface{}) (v expression, err error) { + if op == eq { + if l, ok := x.(value); ok { + if b, ok := l.val.(bool); ok { + if b { // true == y: y + return y.(expression), nil + } + + // false == y: !y + return newUnaryOperation('!', y) + } + } + + if r, ok := y.(value); ok { + if b, ok := r.val.(bool); ok { + if b { // x == true: x + return x.(expression), nil + } + + // x == false: !x + return newUnaryOperation('!', x) + } + } + } + + if op == neq { + if l, ok := x.(value); ok { + if b, ok := l.val.(bool); ok { + if b { // true != y: !y + return newUnaryOperation('!', y) + } + + // false != y: y + return y.(expression), nil + } + } + + if r, ok := y.(value); ok { + if b, ok := r.val.(bool); ok { + if b { // x != true: !x + return newUnaryOperation('!', x) + } + + // x != false: x + return x.(expression), nil + } + } + } + + b := binaryOperation{op, x.(expression), y.(expression)} + var lv interface{} + if e := b.l; e.isStatic() { + if lv, err = e.eval(nil, nil); err != nil { + return nil, err + } + + b.l = value{lv} + } + + if e := b.r; e.isStatic() { + v, err := e.eval(nil, nil) + if err != nil { + return nil, err + } + + if v == nil { + return value{nil}, nil + } + + if op == '/' || op == '%' { + rb := binaryOperation{eq, e, value{idealInt(0)}} + val, err := rb.eval(nil, nil) + if err != nil { + return nil, err + } + + if val.(bool) { + return nil, errDivByZero + } + } + + if b.l.isStatic() && lv == nil { + return value{nil}, nil + } + + b.r = value{v} + } + + if !b.isStatic() { + return &b, nil + } + + val, err := b.eval(nil, nil) + return value{val}, err +} + +func newBinaryOperation(op int, x, y interface{}) (v expression, err error) { + expr, err := newBinaryOperation0(op, x, y) + if err != nil { + return nil, err + } + + b, ok := expr.(*binaryOperation) + if !ok { + return expr, nil + } + + if _, ok := b.l.(*ident); ok { + return expr, nil + } + + if c, ok := b.l.(*call); ok && c.f == "id" { + return expr, nil + } + + var r expression + if r, ok = b.r.(*ident); !ok { + r1, ok := b.r.(*call) + if !ok || r1.f != "id" || len(r1.arg) != 0 { + return expr, nil + } + + r = r1 + } + + // Normalize expr relOp indent: ident invRelOp expr + switch b.op { + case '<': + return &binaryOperation{'>', r, b.l}, nil + case le: + return &binaryOperation{ge, r, b.l}, nil + case '>': + return &binaryOperation{'<', r, b.l}, nil + case ge: + return &binaryOperation{le, r, b.l}, nil + case eq, neq: + return &binaryOperation{b.op, r, b.l}, nil + default: + return expr, nil + } +} + +func (o *binaryOperation) isIdentRelOpVal() (bool, string, interface{}, error) { + sid := "" + id, ok := o.l.(*ident) + if !ok { + f, ok := o.l.(*call) + if !ok || f.f != "id" || len(f.arg) != 0 { + return false, "", nil, nil + } + + sid = "id()" + } else { + if id.isQualified() { + return false, "", nil, nil + } + + sid = id.s + } + + if v, ok := o.r.(value); ok { + switch o.op { + case '<', + le, + '>', + ge, + eq, + neq: + return true, sid, v.val, nil + default: + return false, "", nil, nil + } + } + + return false, "", nil, nil +} + +func (o *binaryOperation) clone(arg []interface{}, unqualify ...string) (expression, error) { + l, err := o.l.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + r, err := o.r.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + return newBinaryOperation(o.op, l, r) +} + +func (o *binaryOperation) isStatic() bool { return o.l.isStatic() && o.r.isStatic() } + +func (o *binaryOperation) String() string { + return fmt.Sprintf("%s %s %s", o.l, iop(o.op), o.r) +} + +func (o *binaryOperation) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (r interface{}, err error) { + defer func() { + if e := recover(); e != nil { + switch x := e.(type) { + case error: + r, err = nil, x + default: + r, err = nil, fmt.Errorf("%v", x) + } + } + }() + + switch op := o.op; op { + case andand: + a, err := expand1(o.l.eval(execCtx, ctx)) + if err != nil { + return nil, err + } + + switch x := a.(type) { + case nil: + b, err := expand1(o.r.eval(execCtx, ctx)) + if err != nil { + return nil, err + } + + switch y := b.(type) { + case nil: + return nil, nil + case bool: + if !y { + return false, nil + } + + return nil, nil + default: + return invOp2(x, y, op) + } + case bool: + if !x { + return false, nil + } + + b, err := expand1(o.r.eval(execCtx, ctx)) + if err != nil { + return nil, err + } + + switch y := b.(type) { + case nil: + return nil, nil + case bool: + return y, nil + default: + return invOp2(x, y, op) + } + default: + return undOp(x, op) + } + case oror: + a, err := expand1(o.l.eval(execCtx, ctx)) + if err != nil { + return nil, err + } + + switch x := a.(type) { + case nil: + b, err := expand1(o.r.eval(execCtx, ctx)) + if err != nil { + return nil, err + } + + switch y := b.(type) { + case nil: + return nil, nil + case bool: + if y { + return y, nil + } + + return nil, nil + default: + return invOp2(x, y, op) + } + case bool: + if x { + return x, nil + } + + b, err := expand1(o.r.eval(execCtx, ctx)) + if err != nil { + return nil, err + } + + switch y := b.(type) { + case nil: + return nil, nil + case bool: + return y, nil + default: + return invOp2(x, y, op) + } + default: + return undOp(x, op) + } + case '>': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + switch y := b.(type) { + case idealFloat: + return x > y, nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return x > y, nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return x > y, nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return x > y, nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + switch y := b.(type) { + case float32: + return x > y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x > y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x > y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x > y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x > y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x > y, nil + default: + return invOp2(x, y, op) + } + case string: + switch y := b.(type) { + case string: + return x > y, nil + default: + return invOp2(x, y, op) + } + case uint8: + switch y := b.(type) { + case uint8: + return x > y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x > y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x > y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x > y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + return x.Cmp(y) > 0, nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + return x.Cmp(y) > 0, nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x > y, nil + default: + return invOp2(x, y, op) + } + case time.Time: + switch y := b.(type) { + case time.Time: + return x.After(y), nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case '<': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + switch y := b.(type) { + case idealFloat: + return x < y, nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return x < y, nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return x < y, nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return x < y, nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + switch y := b.(type) { + case float32: + return x < y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x < y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x < y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x < y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x < y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x < y, nil + default: + return invOp2(x, y, op) + } + case string: + switch y := b.(type) { + case string: + return x < y, nil + default: + return invOp2(x, y, op) + } + case uint8: + switch y := b.(type) { + case uint8: + return x < y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x < y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x < y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x < y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + return x.Cmp(y) < 0, nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + return x.Cmp(y) < 0, nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x < y, nil + default: + return invOp2(x, y, op) + } + case time.Time: + switch y := b.(type) { + case time.Time: + return x.Before(y), nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case le: + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + switch y := b.(type) { + case idealFloat: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + switch y := b.(type) { + case float32: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case string: + switch y := b.(type) { + case string: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case uint8: + switch y := b.(type) { + case uint8: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + return x.Cmp(y) <= 0, nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + return x.Cmp(y) <= 0, nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x <= y, nil + default: + return invOp2(x, y, op) + } + case time.Time: + switch y := b.(type) { + case time.Time: + return x.Before(y) || x.Equal(y), nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case ge: + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + switch y := b.(type) { + case idealFloat: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + switch y := b.(type) { + case float32: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case string: + switch y := b.(type) { + case string: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case uint8: + switch y := b.(type) { + case uint8: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + return x.Cmp(y) >= 0, nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + return x.Cmp(y) >= 0, nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x >= y, nil + default: + return invOp2(x, y, op) + } + case time.Time: + switch y := b.(type) { + case time.Time: + return x.After(y) || x.Equal(y), nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case neq: + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + switch y := b.(type) { + case idealComplex: + return x != y, nil + default: + return invOp2(x, y, op) + } + case idealFloat: + switch y := b.(type) { + case idealFloat: + return x != y, nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return x != y, nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return x != y, nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return x != y, nil + default: + return invOp2(x, y, op) + } + case bool: + switch y := b.(type) { + case bool: + return x != y, nil + default: + return invOp2(x, y, op) + } + case complex64: + switch y := b.(type) { + case complex64: + return x != y, nil + default: + return invOp2(x, y, op) + } + case complex128: + switch y := b.(type) { + case complex128: + return x != y, nil + default: + return invOp2(x, y, op) + } + case float32: + switch y := b.(type) { + case float32: + return x != y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x != y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x != y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x != y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x != y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x != y, nil + default: + return invOp2(x, y, op) + } + case string: + switch y := b.(type) { + case string: + return x != y, nil + default: + return invOp2(x, y, op) + } + case uint8: + switch y := b.(type) { + case uint8: + return x != y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x != y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x != y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x != y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + return x.Cmp(y) != 0, nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + return x.Cmp(y) != 0, nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x != y, nil + default: + return invOp2(x, y, op) + } + case time.Time: + switch y := b.(type) { + case time.Time: + return !x.Equal(y), nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case eq: + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + switch y := b.(type) { + case idealComplex: + return x == y, nil + default: + return invOp2(x, y, op) + } + case idealFloat: + switch y := b.(type) { + case idealFloat: + return x == y, nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return x == y, nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return x == y, nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return x == y, nil + default: + return invOp2(x, y, op) + } + case bool: + switch y := b.(type) { + case bool: + return x == y, nil + default: + return invOp2(x, y, op) + } + case complex64: + switch y := b.(type) { + case complex64: + return x == y, nil + default: + return invOp2(x, y, op) + } + case complex128: + switch y := b.(type) { + case complex128: + return x == y, nil + default: + return invOp2(x, y, op) + } + case float32: + switch y := b.(type) { + case float32: + return x == y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x == y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x == y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x == y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x == y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x == y, nil + default: + return invOp2(x, y, op) + } + case string: + switch y := b.(type) { + case string: + return x == y, nil + default: + return invOp2(x, y, op) + } + case uint8: + switch y := b.(type) { + case uint8: + return x == y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x == y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x == y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x == y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + return x.Cmp(y) == 0, nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + return x.Cmp(y) == 0, nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x == y, nil + default: + return invOp2(x, y, op) + } + case time.Time: + switch y := b.(type) { + case time.Time: + return x.Equal(y), nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case '+': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + switch y := b.(type) { + case idealComplex: + return idealComplex(complex64(x) + complex64(y)), nil + default: + return invOp2(x, y, op) + } + case idealFloat: + switch y := b.(type) { + case idealFloat: + return idealFloat(float64(x) + float64(y)), nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return idealInt(int64(x) + int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return idealRune(int64(x) + int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return idealUint(uint64(x) + uint64(y)), nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + switch y := b.(type) { + case complex64: + return x + y, nil + default: + return invOp2(x, y, op) + } + case complex128: + switch y := b.(type) { + case complex128: + return x + y, nil + default: + return invOp2(x, y, op) + } + case float32: + switch y := b.(type) { + case float32: + return x + y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x + y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x + y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x + y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x + y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x + y, nil + default: + return invOp2(x, y, op) + } + case string: + switch y := b.(type) { + case string: + return x + y, nil + default: + return invOp2(x, y, op) + } + case uint8: + switch y := b.(type) { + case uint8: + return x + y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x + y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x + y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x + y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + var z big.Int + return z.Add(x, y), nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + var z big.Rat + return z.Add(x, y), nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x + y, nil + case time.Time: + return y.Add(x), nil + default: + return invOp2(x, y, op) + } + case time.Time: + switch y := b.(type) { + case time.Duration: + return x.Add(y), nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case '-': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + switch y := b.(type) { + case idealComplex: + return idealComplex(complex64(x) - complex64(y)), nil + default: + return invOp2(x, y, op) + } + case idealFloat: + switch y := b.(type) { + case idealFloat: + return idealFloat(float64(x) - float64(y)), nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return idealInt(int64(x) - int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return idealRune(int64(x) - int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return idealUint(uint64(x) - uint64(y)), nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + switch y := b.(type) { + case complex64: + return x - y, nil + default: + return invOp2(x, y, op) + } + case complex128: + switch y := b.(type) { + case complex128: + return x - y, nil + default: + return invOp2(x, y, op) + } + case float32: + switch y := b.(type) { + case float32: + return x - y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x - y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x - y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x - y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x - y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x - y, nil + default: + return invOp2(x, y, op) + } + case string: + return undOp2(a, b, op) + case uint8: + switch y := b.(type) { + case uint8: + return x - y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x - y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x - y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x - y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + var z big.Int + return z.Sub(x, y), nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + var z big.Rat + return z.Sub(x, y), nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x - y, nil + default: + return invOp2(x, y, op) + } + case time.Time: + switch y := b.(type) { + case time.Duration: + return x.Add(-y), nil + case time.Time: + return x.Sub(y), nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case rsh: + a, b := eval2(o.l, o.r, execCtx, ctx) + if a == nil || b == nil { + return + } + + var cnt uint64 + switch y := b.(type) { + //case nil: + case idealComplex: + return invShiftRHS(a, b) + case idealFloat: + return invShiftRHS(a, b) + case idealInt: + cnt = uint64(y) + case idealRune: + cnt = uint64(y) + case idealUint: + cnt = uint64(y) + case bool: + return invShiftRHS(a, b) + case complex64: + return invShiftRHS(a, b) + case complex128: + return invShiftRHS(a, b) + case float32: + return invShiftRHS(a, b) + case float64: + return invShiftRHS(a, b) + case int8: + return invShiftRHS(a, b) + case int16: + return invShiftRHS(a, b) + case int32: + return invShiftRHS(a, b) + case int64: + return invShiftRHS(a, b) + case string: + return invShiftRHS(a, b) + case uint8: + cnt = uint64(y) + case uint16: + cnt = uint64(y) + case uint32: + cnt = uint64(y) + case uint64: + cnt = uint64(y) + default: + return invOp2(a, b, op) + } + + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + return undOp2(a, b, op) + case idealInt: + return idealInt(int64(x) >> cnt), nil + case idealRune: + return idealRune(int64(x) >> cnt), nil + case idealUint: + return idealUint(uint64(x) >> cnt), nil + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + return undOp2(a, b, op) + case float64: + return undOp2(a, b, op) + case int8: + return x >> cnt, nil + case int16: + return x >> cnt, nil + case int32: + return x >> cnt, nil + case int64: + return x >> cnt, nil + case string: + return undOp2(a, b, op) + case uint8: + return x >> cnt, nil + case uint16: + return x >> cnt, nil + case uint32: + return x >> cnt, nil + case uint64: + return x >> cnt, nil + case *big.Int: + var z big.Int + return z.Rsh(x, uint(cnt)), nil + case time.Duration: + return x >> cnt, nil + default: + return invOp2(a, b, op) + } + case lsh: + a, b := eval2(o.l, o.r, execCtx, ctx) + if a == nil || b == nil { + return + } + + var cnt uint64 + switch y := b.(type) { + //case nil: + case idealComplex: + return invShiftRHS(a, b) + case idealFloat: + return invShiftRHS(a, b) + case idealInt: + cnt = uint64(y) + case idealRune: + cnt = uint64(y) + case idealUint: + cnt = uint64(y) + case bool: + return invShiftRHS(a, b) + case complex64: + return invShiftRHS(a, b) + case complex128: + return invShiftRHS(a, b) + case float32: + return invShiftRHS(a, b) + case float64: + return invShiftRHS(a, b) + case int8: + return invShiftRHS(a, b) + case int16: + return invShiftRHS(a, b) + case int32: + return invShiftRHS(a, b) + case int64: + return invShiftRHS(a, b) + case string: + return invShiftRHS(a, b) + case uint8: + cnt = uint64(y) + case uint16: + cnt = uint64(y) + case uint32: + cnt = uint64(y) + case uint64: + cnt = uint64(y) + default: + return invOp2(a, b, op) + } + + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + return undOp2(a, b, op) + case idealInt: + return idealInt(int64(x) << cnt), nil + case idealRune: + return idealRune(int64(x) << cnt), nil + case idealUint: + return idealUint(uint64(x) << cnt), nil + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + return undOp2(a, b, op) + case float64: + return undOp2(a, b, op) + case int8: + return x << cnt, nil + case int16: + return x << cnt, nil + case int32: + return x << cnt, nil + case int64: + return x << cnt, nil + case string: + return undOp2(a, b, op) + case uint8: + return x << cnt, nil + case uint16: + return x << cnt, nil + case uint32: + return x << cnt, nil + case uint64: + return x << cnt, nil + case *big.Int: + var z big.Int + return z.Lsh(x, uint(cnt)), nil + case time.Duration: + return x << cnt, nil + default: + return invOp2(a, b, op) + } + case '&': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + return undOp2(a, b, op) + case idealInt: + switch y := b.(type) { + case idealInt: + return idealInt(int64(x) & int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return idealRune(int64(x) & int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return idealUint(uint64(x) & uint64(y)), nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + return undOp2(a, b, op) + case float64: + return undOp2(a, b, op) + case int8: + switch y := b.(type) { + case int8: + return x & y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x & y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x & y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x & y, nil + default: + return invOp2(x, y, op) + } + case string: + return undOp2(a, b, op) + case uint8: + switch y := b.(type) { + case uint8: + return x & y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x & y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x & y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x & y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + var z big.Int + return z.And(x, y), nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x & y, nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case '|': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + return undOp2(a, b, op) + case idealInt: + switch y := b.(type) { + case idealInt: + return idealInt(int64(x) | int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return idealRune(int64(x) | int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return idealUint(uint64(x) | uint64(y)), nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + return undOp2(a, b, op) + case float64: + return undOp2(a, b, op) + case int8: + switch y := b.(type) { + case int8: + return x | y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x | y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x | y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x | y, nil + default: + return invOp2(x, y, op) + } + case string: + return undOp2(a, b, op) + case uint8: + switch y := b.(type) { + case uint8: + return x | y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x | y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x | y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x | y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + var z big.Int + return z.Or(x, y), nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x | y, nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case andnot: + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + return undOp2(a, b, op) + case idealInt: + switch y := b.(type) { + case idealInt: + return idealInt(int64(x) &^ int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return idealRune(int64(x) &^ int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return idealUint(uint64(x) &^ uint64(y)), nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + return undOp2(a, b, op) + case float64: + return undOp2(a, b, op) + case int8: + switch y := b.(type) { + case int8: + return x &^ y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x &^ y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x &^ y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x &^ y, nil + default: + return invOp2(x, y, op) + } + case string: + return undOp2(a, b, op) + case uint8: + switch y := b.(type) { + case uint8: + return x &^ y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x &^ y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x &^ y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x &^ y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + var z big.Int + return z.AndNot(x, y), nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x &^ y, nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case '^': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + return undOp2(a, b, op) + case idealInt: + switch y := b.(type) { + case idealInt: + return idealInt(int64(x) ^ int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return idealRune(int64(x) ^ int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return idealUint(uint64(x) ^ uint64(y)), nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + return undOp2(a, b, op) + case float64: + return undOp2(a, b, op) + case int8: + switch y := b.(type) { + case int8: + return x ^ y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x ^ y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x ^ y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x ^ y, nil + default: + return invOp2(x, y, op) + } + case string: + return undOp2(a, b, op) + case uint8: + switch y := b.(type) { + case uint8: + return x ^ y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x ^ y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x ^ y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x ^ y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + var z big.Int + return z.Xor(x, y), nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x ^ y, nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case '%': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp2(a, b, op) + case idealFloat: + return undOp2(a, b, op) + case idealInt: + switch y := b.(type) { + case idealInt: + return idealInt(int64(x) % int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return idealRune(int64(x) % int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return idealUint(uint64(x) % uint64(y)), nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + return undOp2(a, b, op) + case complex128: + return undOp2(a, b, op) + case float32: + return undOp2(a, b, op) + case float64: + return undOp2(a, b, op) + case int8: + switch y := b.(type) { + case int8: + return x % y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x % y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x % y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x % y, nil + default: + return invOp2(x, y, op) + } + case string: + return undOp2(a, b, op) + case uint8: + switch y := b.(type) { + case uint8: + return x % y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x % y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x % y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x % y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + if y.Sign() == 0 { + return nil, errDivByZero + } + + var z big.Int + return z.Mod(x, y), nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x % y, nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case '/': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + switch y := b.(type) { + case idealComplex: + return idealComplex(complex64(x) / complex64(y)), nil + default: + return invOp2(x, y, op) + } + case idealFloat: + switch y := b.(type) { + case idealFloat: + return idealFloat(float64(x) / float64(y)), nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return idealInt(int64(x) / int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return idealRune(int64(x) / int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return idealUint(uint64(x) / uint64(y)), nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + switch y := b.(type) { + case complex64: + return x / y, nil + default: + return invOp2(x, y, op) + } + case complex128: + switch y := b.(type) { + case complex128: + return x / y, nil + default: + return invOp2(x, y, op) + } + case float32: + switch y := b.(type) { + case float32: + return x / y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x / y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x / y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x / y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x / y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x / y, nil + default: + return invOp2(x, y, op) + } + case string: + return undOp2(a, b, op) + case uint8: + switch y := b.(type) { + case uint8: + return x / y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x / y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x / y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x / y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + if y.Sign() == 0 { + return nil, errDivByZero + } + + var z big.Int + return z.Quo(x, y), nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + if y.Sign() == 0 { + return nil, errDivByZero + } + + var z big.Rat + return z.Quo(x, y), nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x / y, nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + case '*': + a, b := o.get2(execCtx, ctx) + if a == nil || b == nil { + return + } + switch x := a.(type) { + //case nil: + case idealComplex: + switch y := b.(type) { + case idealComplex: + return idealComplex(complex64(x) * complex64(y)), nil + default: + return invOp2(x, y, op) + } + case idealFloat: + switch y := b.(type) { + case idealFloat: + return idealFloat(float64(x) * float64(y)), nil + default: + return invOp2(x, y, op) + } + case idealInt: + switch y := b.(type) { + case idealInt: + return idealInt(int64(x) * int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealRune: + switch y := b.(type) { + case idealRune: + return idealRune(int64(x) * int64(y)), nil + default: + return invOp2(x, y, op) + } + case idealUint: + switch y := b.(type) { + case idealUint: + return idealUint(uint64(x) * uint64(y)), nil + default: + return invOp2(x, y, op) + } + case bool: + return undOp2(a, b, op) + case complex64: + switch y := b.(type) { + case complex64: + return x * y, nil + default: + return invOp2(x, y, op) + } + case complex128: + switch y := b.(type) { + case complex128: + return x * y, nil + default: + return invOp2(x, y, op) + } + case float32: + switch y := b.(type) { + case float32: + return x * y, nil + default: + return invOp2(x, y, op) + } + case float64: + switch y := b.(type) { + case float64: + return x * y, nil + default: + return invOp2(x, y, op) + } + case int8: + switch y := b.(type) { + case int8: + return x * y, nil + default: + return invOp2(x, y, op) + } + case int16: + switch y := b.(type) { + case int16: + return x * y, nil + default: + return invOp2(x, y, op) + } + case int32: + switch y := b.(type) { + case int32: + return x * y, nil + default: + return invOp2(x, y, op) + } + case int64: + switch y := b.(type) { + case int64: + return x * y, nil + default: + return invOp2(x, y, op) + } + case string: + return undOp2(a, b, op) + case uint8: + switch y := b.(type) { + case uint8: + return x * y, nil + default: + return invOp2(x, y, op) + } + case uint16: + switch y := b.(type) { + case uint16: + return x * y, nil + default: + return invOp2(x, y, op) + } + case uint32: + switch y := b.(type) { + case uint32: + return x * y, nil + default: + return invOp2(x, y, op) + } + case uint64: + switch y := b.(type) { + case uint64: + return x * y, nil + default: + return invOp2(x, y, op) + } + case *big.Int: + switch y := b.(type) { + case *big.Int: + var z big.Int + return z.Mul(x, y), nil + default: + return invOp2(x, y, op) + } + case *big.Rat: + switch y := b.(type) { + case *big.Rat: + var z big.Rat + return z.Mul(x, y), nil + default: + return invOp2(x, y, op) + } + case time.Duration: + switch y := b.(type) { + case time.Duration: + return x * y, nil + default: + return invOp2(x, y, op) + } + default: + return invOp2(a, b, op) + } + default: + panic("internal error 037") + } +} + +func (o *binaryOperation) get2(execCtx *execCtx, ctx map[interface{}]interface{}) (x, y interface{}) { + x, y = eval2(o.l, o.r, execCtx, ctx) + //dbg("get2 pIn - ", x, y) + //defer func() {dbg("get2 coerced ", x, y)}() + return coerce(x, y) +} + +type ident struct { + s string +} + +func (i *ident) clone(arg []interface{}, unqualify ...string) (expression, error) { + x := strings.IndexByte(i.s, '.') + if x < 0 { + return &ident{s: i.s}, nil + } + + q := i.s[:x] + for _, v := range unqualify { + if q == v { + return &ident{i.s[x+1:]}, nil + } + } + return &ident{s: i.s}, nil +} + +func (i *ident) isQualified() bool { return strings.Contains(i.s, ".") } + +func (i *ident) isStatic() bool { return false } + +func (i *ident) String() string { return i.s } + +func (i *ident) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + if _, ok := ctx["$agg0"]; ok { + return int64(0), nil + } + + //defer func() { dbg("ident %q -> %v %v", i.s, v, err) }() + v, ok := ctx[i.s] + if !ok { + err = fmt.Errorf("unknown field %s", i.s) + } + return +} + +type pInEval struct { + m map[interface{}]struct{} // IN (SELECT...) results + sample interface{} +} + +type pIn struct { + expr expression + list []expression + not bool + sel *selectStmt +} + +func (n *pIn) clone(arg []interface{}, unqualify ...string) (expression, error) { + expr, err := n.expr.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + list, err := cloneExpressionList(arg, n.list) + if err != nil { + return nil, err + } + + return &pIn{ + expr: expr, + list: list, + not: n.not, + sel: n.sel, + }, nil +} + +func (n *pIn) isStatic() bool { + if !n.expr.isStatic() || n.sel != nil { + return false + } + + for _, v := range n.list { + if !v.isStatic() { + return false + } + } + return true +} + +//LATER newIn + +func (n *pIn) String() string { + if n.sel == nil { + a := []string{} + for _, v := range n.list { + a = append(a, v.String()) + } + if n.not { + return fmt.Sprintf("%s NOT IN (%s)", n.expr, strings.Join(a, ",")) + } + + return fmt.Sprintf("%s IN (%s)", n.expr, strings.Join(a, ",")) + } + + if n.not { + return fmt.Sprintf("%s NOT IN (%s)", n.expr, n.sel) + } + + return fmt.Sprintf("%s IN (%s)", n.expr, n.sel) +} + +func (n *pIn) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + lhs, err := expand1(n.expr.eval(execCtx, ctx)) + if err != nil { + return nil, err + } + + if lhs == nil { + return nil, nil //TODO Add test for NULL LHS. + } + + if n.sel == nil { + for _, v := range n.list { + b, err := newBinaryOperation(eq, value{lhs}, v) + if err != nil { + return nil, err + } + + eval, err := b.eval(execCtx, ctx) + if err != nil { + return nil, err + } + + if x, ok := eval.(bool); ok && x { + return !n.not, nil + } + } + return n.not, nil + } + + var ev *pInEval + ev0 := ctx[n] + if ev0 == nil { // SELECT not yet evaluated. + r, err := n.sel.plan(execCtx) + if err != nil { + return nil, err + } + + if g, e := len(r.fieldNames()), 1; g != e { + return false, fmt.Errorf("IN (%s): mismatched field count, have %d, need %d", n.sel, g, e) + } + + ev = &pInEval{m: map[interface{}]struct{}{}} + ctx[n] = ev + m := ev.m + typechecked := false + if err := r.do(execCtx, func(id interface{}, data []interface{}) (more bool, err error) { + if typechecked { + if data[0] == nil { + return true, nil + } + + m[data[0]] = struct{}{} + } + + if data[0] == nil { + return true, nil + } + + ev.sample = data[0] + switch ev.sample.(type) { + case bool, byte, complex128, complex64, float32, + float64, int16, int32, int64, int8, + string, uint16, uint32, uint64: + typechecked = true + m[ev.sample] = struct{}{} + return true, nil + default: + return false, fmt.Errorf("IN (%s): invalid field type: %T", n.sel, data[0]) + } + + }); err != nil { + return nil, err + } + } else { + ev = ev0.(*pInEval) + } + + if ev.sample == nil { + return nil, nil + } + + _, ok := ev.m[coerce1(lhs, ev.sample)] + return ok != n.not, nil +} + +type value struct { + val interface{} +} + +func (l value) clone(arg []interface{}, unqualify ...string) (expression, error) { + return value{val: l.val}, nil +} + +func (l value) isStatic() bool { return true } + +func (l value) String() string { + switch x := l.val.(type) { + case nil: + return "NULL" + case idealComplex: + s := fmt.Sprint(x) + return s[1 : len(s)-1] + case complex64: + s := fmt.Sprint(x) + return s[1 : len(s)-1] + case complex128: + s := fmt.Sprint(x) + return s[1 : len(s)-1] + case string: + return fmt.Sprintf("%q", x) + case time.Duration: + return fmt.Sprintf("duration(%q)", l.val) + case time.Time: + return fmt.Sprintf("time(%q)", l.val) + case *big.Rat: + return fmt.Sprintf("bigrat(%q)", l.val) + case *big.Int: + return fmt.Sprintf(`bigint("%v")`, l.val) + default: + return fmt.Sprintf("%v", l.val) + } +} + +func (l value) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (interface{}, error) { + return l.val, nil +} + +type conversion struct { + typ int + val expression +} + +func (c *conversion) clone(arg []interface{}, unqualify ...string) (expression, error) { + val, err := c.val.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + return &conversion{typ: c.typ, val: val}, nil +} + +func (c *conversion) isStatic() bool { + return c.val.isStatic() +} + +//LATER newConversion or fake unary op + +func (c *conversion) String() string { + return fmt.Sprintf("%s(%s)", typeStr(c.typ), c.val) +} + +func (c *conversion) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + val, err := expand1(c.val.eval(execCtx, ctx)) + if err != nil { + return + } + + return convert(val, c.typ) +} + +type unaryOperation struct { + op int + v expression +} + +func newUnaryOperation(op int, x interface{}) (v expression, err error) { + l, ok := x.(expression) + if !ok { + panic("internal error 038") + } + + for { + pe, ok := l.(*pexpr) + if ok { + l = pe.expr + continue + } + + break + } + + if l.isStatic() { + val, err := l.eval(nil, nil) + if err != nil { + return nil, err + } + + l = value{val} + } + + if op == '!' { + b, ok := l.(*binaryOperation) + if ok { + switch b.op { + case eq: + b.op = neq + return b, nil + case neq: + b.op = eq + return b, nil + case '>': + b.op = le + return b, nil + case ge: + b.op = '<' + return b, nil + case '<': + b.op = ge + return b, nil + case le: + b.op = '>' + return b, nil + } + } + + u, ok := l.(*unaryOperation) + if ok && u.op == '!' { // !!x: x + return u.v, nil + } + } + + return &unaryOperation{op, l}, nil +} + +func (u *unaryOperation) clone(arg []interface{}, unqualify ...string) (expression, error) { + v, err := u.v.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + return &unaryOperation{op: u.op, v: v}, nil +} + +func (u *unaryOperation) isStatic() bool { return u.v.isStatic() } + +func (u *unaryOperation) String() string { + switch u.v.(type) { + case *binaryOperation: + return fmt.Sprintf("%s(%s)", iop(u.op), u.v) + default: + return fmt.Sprintf("%s%s", iop(u.op), u.v) + } +} + +// !ident +func (u *unaryOperation) isNotQIdent() (bool, string, expression) { + if u.op != '!' { + return false, "", nil + } + + id, ok := u.v.(*ident) + if ok && id.isQualified() { + return true, mustQualifier(id.s), &unaryOperation{'!', &ident{mustSelector(id.s)}} + } + + return false, "", nil +} + +func (u *unaryOperation) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (r interface{}, err error) { + defer func() { + if e := recover(); e != nil { + switch x := e.(type) { + case error: + r, err = nil, x + default: + r, err = nil, fmt.Errorf("%v", x) + } + } + }() + + switch op := u.op; op { + case '!': + a := eval(u.v, execCtx, ctx) + if a == nil { + return + } + + switch x := a.(type) { + case bool: + return !x, nil + default: + return undOp(a, op) + } + case '^': + a := eval(u.v, execCtx, ctx) + if a == nil { + return + } + + switch x := a.(type) { + //case nil: + case idealComplex: + return undOp(a, op) + case idealFloat: + return undOp(a, op) + case idealInt: + return ^x, nil + case idealRune: + return ^x, nil + case idealUint: + return ^x, nil + case bool: + return undOp(a, op) + case complex64: + return undOp(a, op) + case complex128: + return undOp(a, op) + case float32: + return undOp(a, op) + case float64: + return undOp(a, op) + case int8: + return ^x, nil + case int16: + return ^x, nil + case int32: + return ^x, nil + case int64: + return ^x, nil + case string: + return undOp(a, op) + case uint8: + return ^x, nil + case uint16: + return ^x, nil + case uint32: + return ^x, nil + case uint64: + return ^x, nil + case *big.Int: + var z big.Int + return z.Not(x), nil + case time.Duration: + return ^x, nil + default: + return undOp(a, op) + } + case '+': + a := eval(u.v, execCtx, ctx) + if a == nil { + return + } + + switch x := a.(type) { + //case nil: + case idealComplex: + return +x, nil + case idealFloat: + return +x, nil + case idealInt: + return +x, nil + case idealRune: + return +x, nil + case idealUint: + return +x, nil + case bool: + return undOp(a, op) + case complex64: + return +x, nil + case complex128: + return +x, nil + case float32: + return +x, nil + case float64: + return +x, nil + case int8: + return +x, nil + case int16: + return +x, nil + case int32: + return +x, nil + case int64: + return +x, nil + case string: + return undOp(a, op) + case uint8: + return +x, nil + case uint16: + return +x, nil + case uint32: + return +x, nil + case uint64: + return +x, nil + case *big.Int: + var z big.Int + return z.Set(x), nil + case *big.Rat: + var z big.Rat + return z.Set(x), nil + case time.Duration: + return x, nil + default: + return undOp(a, op) + } + case '-': + a := eval(u.v, execCtx, ctx) + if a == nil { + return + } + + switch x := a.(type) { + //case nil: + case idealComplex: + return -x, nil + case idealFloat: + return -x, nil + case idealInt: + return -x, nil + case idealRune: + return -x, nil + case idealUint: + return -x, nil + case bool: + return undOp(a, op) + case complex64: + return -x, nil + case complex128: + return -x, nil + case float32: + return -x, nil + case float64: + return -x, nil + case int8: + return -x, nil + case int16: + return -x, nil + case int32: + return -x, nil + case int64: + return -x, nil + case string: + return undOp(a, op) + case uint8: + return -x, nil + case uint16: + return -x, nil + case uint32: + return -x, nil + case uint64: + return -x, nil + case *big.Int: + var z big.Int + return z.Neg(x), nil + case *big.Rat: + var z big.Rat + return z.Neg(x), nil + case time.Duration: + return -x, nil + default: + return undOp(a, op) + } + default: + panic("internal error 039") + } +} + +type call struct { + f string + arg []expression +} + +func newCall(f string, arg []expression) (v expression, isAgg bool, err error) { + x := builtin[f] + if x.f == nil { + return nil, false, fmt.Errorf("undefined: %s", f) + } + + isAgg = x.isAggregate + if g, min, max := len(arg), x.minArgs, x.maxArgs; g < min || g > max { + a := []interface{}{} + for _, v := range arg { + a = append(a, v) + } + return nil, false, badNArgs(min, f, a) + } + + c := call{f: f} + for _, val := range arg { + if !val.isStatic() { + c.arg = append(c.arg, val) + continue + } + + eval, err := val.eval(nil, nil) + if err != nil { + return nil, isAgg, err + } + + c.arg = append(c.arg, value{eval}) + } + + return &c, isAgg, nil +} + +func (c *call) clone(arg []interface{}, unqualify ...string) (expression, error) { + list, err := cloneExpressionList(arg, c.arg) + if err != nil { + return nil, err + } + + return &call{f: c.f, arg: list}, nil +} + +func (c *call) isStatic() bool { + v := builtin[c.f] + if v.f == nil || !v.isStatic { + return false + } + + for _, v := range c.arg { + if !v.isStatic() { + return false + } + } + return true +} + +func (c *call) String() string { + a := []string{} + for _, v := range c.arg { + a = append(a, v.String()) + } + return fmt.Sprintf("%s(%s)", c.f, strings.Join(a, ", ")) +} + +func (c *call) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + f, ok := builtin[c.f] + if !ok { + return nil, fmt.Errorf("unknown function %s", c.f) + } + + isID := c.f == "id" + a := make([]interface{}, len(c.arg)) + for i, arg := range c.arg { + if v, err = expand1(arg.eval(execCtx, ctx)); err != nil { + if !isID { + return nil, err + } + + if _, ok := arg.(*ident); !ok { + return nil, err + } + + a[i] = arg + continue + } + + a[i] = v + } + + if ctx != nil { + ctx["$fn"] = c + } + return f.f(a, ctx) +} + +type parameter struct { + n int +} + +func (p parameter) clone(arg []interface{}, unqualify ...string) (expression, error) { + i := p.n - 1 + if i < len(arg) { + return value{val: arg[i]}, nil + } + + return nil, fmt.Errorf("missing %s", p) +} + +func (parameter) isStatic() bool { return false } + +func (p parameter) String() string { return fmt.Sprintf("$%d", p.n) } + +func (p parameter) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + i := p.n - 1 + if i < len(execCtx.arg) { + return execCtx.arg[i], nil + } + + return nil, fmt.Errorf("missing %s", p) +} + +//MAYBE make it an unary operation +type isNull struct { + expr expression + not bool +} + +//LATER newIsNull + +func (is *isNull) clone(arg []interface{}, unqualify ...string) (expression, error) { + expr, err := is.expr.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + return &isNull{expr: expr, not: is.not}, nil +} + +func (is *isNull) isStatic() bool { return is.expr.isStatic() } + +func (is *isNull) String() string { + if is.not { + return fmt.Sprintf("%s IS NOT NULL", is.expr) + } + + return fmt.Sprintf("%s IS NULL", is.expr) +} + +func (is *isNull) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + val, err := is.expr.eval(execCtx, ctx) + if err != nil { + return + } + + return val == nil != is.not, nil +} + +type indexOp struct { + expr, x expression +} + +func newIndex(sv, xv expression) (v expression, err error) { + s, fs, i := "", false, uint64(0) + x := indexOp{sv, xv} + if x.expr.isStatic() { + v, err := x.expr.eval(nil, nil) + if err != nil { + return nil, err + } + + if v == nil { + return value{nil}, nil + } + + if s, fs = v.(string); !fs { + return nil, invXOp(sv, xv) + } + + x.expr = value{s} + } + + if x.x.isStatic() { + v, err := x.x.eval(nil, nil) + if err != nil { + return nil, err + } + + if v == nil { + return value{nil}, nil + } + + var p *string + if fs { + p = &s + } + if i, err = indexExpr(p, v); err != nil { + return nil, err + } + + x.x = value{i} + } + + return &x, nil +} + +func (x *indexOp) clone(arg []interface{}, unqualify ...string) (expression, error) { + expr, err := x.expr.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + x2, err := x.x.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + return &indexOp{expr: expr, x: x2}, nil +} + +func (x *indexOp) isStatic() bool { + return x.expr.isStatic() && x.x.isStatic() +} + +func (x *indexOp) String() string { return fmt.Sprintf("%s[%s]", x.expr, x.x) } + +func (x *indexOp) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + s0, err := x.expr.eval(execCtx, ctx) + if err != nil { + return nil, runErr(err) + } + + s, ok := s0.(string) + if !ok { + return nil, runErr(invXOp(s0, x.x)) + } + + i0, err := x.x.eval(execCtx, ctx) + if err != nil { + return nil, runErr(err) + } + + if i0 == nil { + return nil, nil + } + + i, err := indexExpr(&s, i0) + if err != nil { + return nil, runErr(err) + } + + return s[i], nil +} + +type slice struct { + expr expression + lo, hi *expression +} + +func newSlice(e expression, lo, hi *expression) (v expression, err error) { + y := slice{e, lo, hi} + var val interface{} + if e := y.expr; e.isStatic() { + if val, err = e.eval(nil, nil); err != nil { + return nil, err + } + + if val == nil { + return value{nil}, nil + } + + y.expr = value{val} + } + + if p := y.lo; p != nil { + if e := expr(*p); e.isStatic() { + if val, err = e.eval(nil, nil); err != nil { + return nil, err + } + + if val == nil { + return value{nil}, nil + } + + v := expression(value{val}) + y.lo = &v + } + } + + if p := y.hi; p != nil { + if e := expr(*p); e.isStatic() { + if val, err = e.eval(nil, nil); err != nil { + return nil, err + } + + if val == nil { + return value{nil}, nil + } + + v := expression(value{val}) + y.hi = &v + } + } + return &y, nil +} + +func (s *slice) clone(arg []interface{}, unqualify ...string) (expression, error) { + expr, err := s.expr.clone(arg, unqualify...) + if err != nil { + return nil, err + } + + r := &slice{expr: expr, lo: s.lo, hi: s.hi} + if s.lo != nil { + e, err := (*s.lo).clone(arg, unqualify...) + if err != nil { + return nil, err + } + + r.lo = &e + } + if s.hi != nil { + e, err := (*s.hi).clone(arg, unqualify...) + if err != nil { + return nil, err + } + + r.hi = &e + } + return r, nil +} + +func (s *slice) eval(execCtx *execCtx, ctx map[interface{}]interface{}) (v interface{}, err error) { + s0, err := s.expr.eval(execCtx, ctx) + if err != nil { + return + } + + if s0 == nil { + return + } + + ss, ok := s0.(string) + if !ok { + return nil, runErr(invSOp(s0)) + } + + var iLo, iHi uint64 + if s.lo != nil { + i, err := (*s.lo).eval(execCtx, ctx) + if err != nil { + return nil, err + } + + if i == nil { + return nil, err + } + + if iLo, err = sliceExpr(&ss, i, 0); err != nil { + return nil, err + } + } + + iHi = uint64(len(ss)) + if s.hi != nil { + i, err := (*s.hi).eval(execCtx, ctx) + if err != nil { + return nil, err + } + + if i == nil { + return nil, err + } + + if iHi, err = sliceExpr(&ss, i, 1); err != nil { + return nil, err + } + } + + return ss[iLo:iHi], nil +} + +func (s *slice) isStatic() bool { + if !s.expr.isStatic() { + return false + } + + if p := s.lo; p != nil && !(*p).isStatic() { + return false + } + + if p := s.hi; p != nil && !(*p).isStatic() { + return false + } + + return false +} + +func (s *slice) String() string { + switch { + case s.lo == nil && s.hi == nil: + return fmt.Sprintf("%v[:]", s.expr) + case s.lo == nil && s.hi != nil: + return fmt.Sprintf("%v[:%v]", s.expr, *s.hi) + case s.lo != nil && s.hi == nil: + return fmt.Sprintf("%v[%v:]", s.expr, *s.lo) + default: //case s.lo != nil && s.hi != nil: + return fmt.Sprintf("%v[%v:%v]", s.expr, *s.lo, *s.hi) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/file.go b/Godeps/_workspace/src/github.com/cznic/ql/file.go new file mode 100644 index 000000000..d3ce3d233 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/file.go @@ -0,0 +1,1279 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Well known handles +// 1: root +// 2: id + +package ql + +import ( + "crypto/sha1" + "fmt" + "io" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "sync" + "time" + + "github.com/camlistore/lock" + "github.com/cznic/exp/lldb" + "github.com/cznic/mathutil" +) + +const ( + magic = "\x60\xdbql" +) + +var ( + _ btreeIndex = (*fileIndex)(nil) + _ btreeIterator = (*fileBTreeIterator)(nil) + _ indexIterator = (*fileIndexIterator)(nil) + _ storage = (*file)(nil) + _ temp = (*fileTemp)(nil) +) + +type chunk struct { // expanded to blob types lazily + f *file + b []byte +} + +func (c chunk) expand() (v interface{}, err error) { + return c.f.loadChunks(c.b) +} + +func expand1(data interface{}, e error) (v interface{}, err error) { + if e != nil { + return nil, e + } + + c, ok := data.(chunk) + if !ok { + return data, nil + } + + return c.expand() +} + +func expand(data []interface{}) (err error) { + for i, v := range data { + if data[i], err = expand1(v, nil); err != nil { + return + } + } + return +} + +// OpenFile returns a DB backed by a named file. The back end limits the size +// of a record to about 64 kB. +func OpenFile(name string, opt *Options) (db *DB, err error) { + var f lldb.OSFile + if f = opt.OSFile; f == nil { + f, err = os.OpenFile(name, os.O_RDWR, 0666) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + + if !opt.CanCreate { + return nil, err + } + + f, err = os.OpenFile(name, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666) + if err != nil { + return nil, err + } + } + } + + fi, err := newFileFromOSFile(f) // always ACID + if err != nil { + return + } + + if fi.tempFile = opt.TempFile; fi.tempFile == nil { + fi.tempFile = func(dir, prefix string) (f lldb.OSFile, err error) { + f0, err := ioutil.TempFile(dir, prefix) + return f0, err + } + } + + return newDB(fi) +} + +// Options amend the behavior of OpenFile. +// +// CanCreate +// +// The CanCreate option enables OpenFile to create the DB file if it does not +// exists. +// +// OSFile +// +// OSFile allows to pass an os.File like back end providing, for example, +// encrypted storage. If this field is nil then OpenFile uses the file named by +// the 'name' parameter instead. +// +// TempFile +// +// TempFile provides a temporary file used for evaluating the GROUP BY, ORDER +// BY, ... clauses. The hook is intended to be used by encrypted DB back ends +// to avoid leaks of unecrypted data to such temp files by providing temp files +// which are encrypted as well. Note that *os.File satisfies the lldb.OSFile +// interface. +// +// If TempFile is nil it defaults to ioutil.TempFile. +type Options struct { + CanCreate bool + OSFile lldb.OSFile + TempFile func(dir, prefix string) (f lldb.OSFile, err error) +} + +type fileBTreeIterator struct { + en *lldb.BTreeEnumerator + t *fileTemp +} + +func (it *fileBTreeIterator) Next() (k, v []interface{}, err error) { + bk, bv, err := it.en.Next() + if err != nil { + return + } + + if k, err = lldb.DecodeScalars(bk); err != nil { + return + } + + for i, val := range k { + b, ok := val.([]byte) + if !ok { + continue + } + + c := chunk{it.t.file, b} + if k[i], err = c.expand(); err != nil { + return nil, nil, err + } + } + + if err = enforce(k, it.t.colsK); err != nil { + return + } + + if v, err = lldb.DecodeScalars(bv); err != nil { + return + } + + for i, val := range v { + b, ok := val.([]byte) + if !ok { + continue + } + + c := chunk{it.t.file, b} + if v[i], err = c.expand(); err != nil { + return nil, nil, err + } + } + + err = enforce(v, it.t.colsV) + return +} + +func enforce(val []interface{}, cols []*col) (err error) { + for i, v := range val { + if val[i], err = convert(v, cols[i].typ); err != nil { + return + } + } + return +} + +//NTYPE +func infer(from []interface{}, to *[]*col) { + if len(*to) == 0 { + *to = make([]*col, len(from)) + for i := range *to { + (*to)[i] = &col{} + } + } + for i, c := range *to { + if f := from[i]; f != nil { + switch x := f.(type) { + //case nil: + case idealComplex: + c.typ = qComplex128 + from[i] = complex128(x) + case idealFloat: + c.typ = qFloat64 + from[i] = float64(x) + case idealInt: + c.typ = qInt64 + from[i] = int64(x) + case idealRune: + c.typ = qInt32 + from[i] = int32(x) + case idealUint: + c.typ = qUint64 + from[i] = uint64(x) + case bool: + c.typ = qBool + case complex128: + c.typ = qComplex128 + case complex64: + c.typ = qComplex64 + case float64: + c.typ = qFloat64 + case float32: + c.typ = qFloat32 + case int8: + c.typ = qInt8 + case int16: + c.typ = qInt16 + case int32: + c.typ = qInt32 + case int64: + c.typ = qInt64 + case string: + c.typ = qString + case uint8: + c.typ = qUint8 + case uint16: + c.typ = qUint16 + case uint32: + c.typ = qUint32 + case uint64: + c.typ = qUint64 + case []byte: + c.typ = qBlob + case *big.Int: + c.typ = qBigInt + case *big.Rat: + c.typ = qBigRat + case time.Time: + c.typ = qTime + case time.Duration: + c.typ = qDuration + case chunk: + vals, err := lldb.DecodeScalars([]byte(x.b)) + if err != nil { + panic(err) + } + + if len(vals) == 0 { + panic("internal error 040") + } + + i, ok := vals[0].(int64) + if !ok { + panic("internal error 041") + } + + c.typ = int(i) + case map[string]interface{}: // map of ids of a cross join + default: + panic("internal error 042") + } + } + } +} + +type fileTemp struct { + *file + colsK []*col + colsV []*col + t *lldb.BTree +} + +func (t *fileTemp) BeginTransaction() error { + return nil +} + +func (t *fileTemp) Get(k []interface{}) (v []interface{}, err error) { + if err = expand(k); err != nil { + return + } + + if err = t.flatten(k); err != nil { + return nil, err + } + + bk, err := lldb.EncodeScalars(k...) + if err != nil { + return + } + + bv, err := t.t.Get(nil, bk) + if err != nil { + return + } + + return lldb.DecodeScalars(bv) +} + +func (t *fileTemp) Drop() (err error) { + if t.f0 == nil { + return + } + + fn := t.f0.Name() + if err = t.f0.Close(); err != nil { + return + } + + if fn == "" { + return + } + + return os.Remove(fn) +} + +func (t *fileTemp) SeekFirst() (it btreeIterator, err error) { + en, err := t.t.SeekFirst() + if err != nil { + return + } + + return &fileBTreeIterator{t: t, en: en}, nil +} + +func (t *fileTemp) Set(k, v []interface{}) (err error) { + if err = expand(k); err != nil { + return + } + + if err = expand(v); err != nil { + return + } + + infer(k, &t.colsK) + infer(v, &t.colsV) + + if err = t.flatten(k); err != nil { + return + } + + bk, err := lldb.EncodeScalars(k...) + if err != nil { + return + } + + if err = t.flatten(v); err != nil { + return + } + + bv, err := lldb.EncodeScalars(v...) + if err != nil { + return + } + + return t.t.Set(bk, bv) +} + +type file struct { + a *lldb.Allocator + codec *gobCoder + f lldb.Filer + f0 lldb.OSFile + id int64 + lck io.Closer + mu sync.Mutex + name string + tempFile func(dir, prefix string) (f lldb.OSFile, err error) + wal *os.File +} + +func newFileFromOSFile(f lldb.OSFile) (fi *file, err error) { + nm := lockName(f.Name()) + lck, err := lock.Lock(nm) + if err != nil { + if lck != nil { + lck.Close() + } + return nil, err + } + + close := true + defer func() { + if close && lck != nil { + lck.Close() + } + }() + + var w *os.File + closew := false + wn := walName(f.Name()) + w, err = os.OpenFile(wn, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666) + closew = true + defer func() { + if closew { + nm := w.Name() + w.Close() + os.Remove(nm) + w = nil + } + }() + + if err != nil { + if !os.IsExist(err) { + return nil, err + } + + closew = false + w, err = os.OpenFile(wn, os.O_RDWR, 0666) + if err != nil { + return nil, err + } + + closew = true + st, err := w.Stat() + if err != nil { + return nil, err + } + + if st.Size() != 0 { + return nil, fmt.Errorf("(file-001) non empty WAL file %s exists", wn) + } + } + + info, err := f.Stat() + if err != nil { + return nil, err + } + + switch sz := info.Size(); { + case sz == 0: + b := make([]byte, 16) + copy(b, []byte(magic)) + if _, err := f.Write(b); err != nil { + return nil, err + } + + filer := lldb.Filer(lldb.NewOSFiler(f)) + filer = lldb.NewInnerFiler(filer, 16) + if filer, err = lldb.NewACIDFiler(filer, w); err != nil { + return nil, err + } + + a, err := lldb.NewAllocator(filer, &lldb.Options{}) + if err != nil { + return nil, err + } + + a.Compress = true + s := &file{ + a: a, + codec: newGobCoder(), + f0: f, + f: filer, + lck: lck, + name: f.Name(), + wal: w, + } + if err = s.BeginTransaction(); err != nil { + return nil, err + } + + h, err := s.Create() + if err != nil { + return nil, err + } + + if h != 1 { // root + panic("internal error 043") + } + + if h, err = s.a.Alloc(make([]byte, 8)); err != nil { + return nil, err + } + + if h != 2 { // id + panic("internal error 044") + } + + close, closew = false, false + return s, s.Commit() + default: + b := make([]byte, 16) + if _, err := f.Read(b); err != nil { + return nil, err + } + + if string(b[:len(magic)]) != magic { + return nil, fmt.Errorf("(file-002) unknown file format") + } + + filer := lldb.Filer(lldb.NewOSFiler(f)) + filer = lldb.NewInnerFiler(filer, 16) + if filer, err = lldb.NewACIDFiler(filer, w); err != nil { + return nil, err + } + + a, err := lldb.NewAllocator(filer, &lldb.Options{}) + if err != nil { + return nil, err + } + + bid, err := a.Get(nil, 2) // id + if err != nil { + return nil, err + } + + if len(bid) != 8 { + return nil, fmt.Errorf("(file-003) corrupted DB: id |% x|", bid) + } + + id := int64(0) + for _, v := range bid { + id = (id << 8) | int64(v) + } + + a.Compress = true + s := &file{ + a: a, + codec: newGobCoder(), + f0: f, + f: filer, + id: id, + lck: lck, + name: f.Name(), + wal: w, + } + + close, closew = false, false + return s, nil + } +} + +func (s *file) OpenIndex(unique bool, handle int64) (btreeIndex, error) { + t, err := lldb.OpenBTree(s.a, s.collate, handle) + if err != nil { + return nil, err + } + + return &fileIndex{s, handle, t, unique}, nil +} + +func (s *file) CreateIndex(unique bool) ( /* handle */ int64, btreeIndex, error) { + t, h, err := lldb.CreateBTree(s.a, s.collate) + if err != nil { + return -1, nil, err + } + + return h, &fileIndex{s, h, t, unique}, nil +} + +func (s *file) Acid() bool { return s.wal != nil } + +func errSet(p *error, errs ...error) (err error) { + err = *p + for _, e := range errs { + if err != nil { + return + } + *p, err = e, e + } + return +} + +func (s *file) lock() func() { + s.mu.Lock() + return s.mu.Unlock +} + +func (s *file) Close() (err error) { + defer s.lock()() + + es := s.f0.Sync() + ef := s.f0.Close() + var ew error + if s.wal != nil { + ew = s.wal.Close() + } + el := s.lck.Close() + return errSet(&err, es, ef, ew, el) +} + +func (s *file) Name() string { return s.name } + +func (s *file) Verify() (allocs int64, err error) { + defer s.lock()() + var stat lldb.AllocStats + if err = s.a.Verify(lldb.NewMemFiler(), nil, &stat); err != nil { + return + } + + allocs = stat.AllocAtoms + return +} + +func (s *file) expandBytes(d []interface{}) (err error) { + for i, v := range d { + b, ok := v.([]byte) + if !ok { + continue + } + + d[i], err = s.loadChunks(b) + if err != nil { + return + } + } + return +} + +func (s *file) collate(a, b []byte) int { //TODO w/ error return + da, err := lldb.DecodeScalars(a) + if err != nil { + panic(err) + } + + if err = s.expandBytes(da); err != nil { + panic(err) + } + + db, err := lldb.DecodeScalars(b) + if err != nil { + panic(err) + } + + if err = s.expandBytes(db); err != nil { + panic(err) + } + + //dbg("da: %v, db: %v", da, db) + return collate(da, db) +} + +func (s *file) CreateTemp(asc bool) (bt temp, err error) { + f, err := s.tempFile("", "ql-tmp-") + if err != nil { + return nil, err + } + + fn := f.Name() + filer := lldb.NewOSFiler(f) + a, err := lldb.NewAllocator(filer, &lldb.Options{}) + if err != nil { + f.Close() + os.Remove(fn) + return nil, err + } + + k := 1 + if !asc { + k = -1 + } + + t, _, err := lldb.CreateBTree(a, func(a, b []byte) int { //TODO w/ error return + return k * s.collate(a, b) + }) + if err != nil { + f.Close() + if fn != "" { + os.Remove(fn) + } + return nil, err + } + + x := &fileTemp{file: &file{ + a: a, + codec: newGobCoder(), + f0: f, + }, + t: t} + return x, nil +} + +func (s *file) BeginTransaction() (err error) { + defer s.lock()() + return s.f.BeginUpdate() +} + +func (s *file) Rollback() (err error) { + defer s.lock()() + return s.f.Rollback() +} + +func (s *file) Commit() (err error) { + defer s.lock()() + return s.f.EndUpdate() +} + +func (s *file) Create(data ...interface{}) (h int64, err error) { + if err = expand(data); err != nil { + return + } + + if err = s.flatten(data); err != nil { + return + } + + b, err := lldb.EncodeScalars(data...) + if err != nil { + return + } + + defer s.lock()() + return s.a.Alloc(b) +} + +func (s *file) Delete(h int64, blobCols ...*col) (err error) { + switch len(blobCols) { + case 0: + defer s.lock()() + return s.a.Free(h) + default: + return s.free(h, blobCols) + } +} + +func (s *file) ResetID() (err error) { + s.id = 0 + return +} + +func (s *file) ID() (int64, error) { + defer s.lock()() + + s.id++ + b := make([]byte, 8) + id := s.id + for i := 7; i >= 0; i-- { + b[i] = byte(id) + id >>= 8 + } + + return s.id, s.a.Realloc(2, b) +} + +func (s *file) free(h int64, blobCols []*col) (err error) { + b, err := s.a.Get(nil, h) //LATER +bufs + if err != nil { + return + } + + rec, err := lldb.DecodeScalars(b) + if err != nil { + return + } + + for _, col := range blobCols { + if col.index >= len(rec) { + return fmt.Errorf("(file-004) file.free: corrupted DB (record len)") + } + if col.index+2 >= len(rec) { + continue + } + + switch x := rec[col.index+2].(type) { + case nil: + // nop + case []byte: + if err = s.freeChunks(x); err != nil { + return + } + } + } + defer s.lock()() + return s.a.Free(h) +} + +func (s *file) Read(dst []interface{}, h int64, cols ...*col) (data []interface{}, err error) { //NTYPE + b, err := s.a.Get(nil, h) //LATER +bufs + if err != nil { + return + } + + rec, err := lldb.DecodeScalars(b) + if err != nil { + return + } + + for _, col := range cols { + i := col.index + 2 + if i >= len(rec) || rec[i] == nil { + continue + } + + switch col.typ { + case 0: + case qBool: + case qComplex64: + rec[i] = complex64(rec[i].(complex128)) + case qComplex128: + case qFloat32: + rec[i] = float32(rec[i].(float64)) + case qFloat64: + case qInt8: + rec[i] = int8(rec[i].(int64)) + case qInt16: + rec[i] = int16(rec[i].(int64)) + case qInt32: + rec[i] = int32(rec[i].(int64)) + case qInt64: + case qString: + case qUint8: + rec[i] = uint8(rec[i].(uint64)) + case qUint16: + rec[i] = uint16(rec[i].(uint64)) + case qUint32: + rec[i] = uint32(rec[i].(uint64)) + case qUint64: + case qBlob, qBigInt, qBigRat, qTime, qDuration: + switch x := rec[i].(type) { + case []byte: + rec[i] = chunk{f: s, b: x} + default: + return nil, fmt.Errorf("(file-006) corrupted DB: non nil chunk type is not []byte") + } + default: + panic("internal error 045") + } + } + + if cols != nil { + for n, dn := len(cols)+2, len(rec); dn < n; dn++ { + rec = append(rec, nil) + } + } + + return rec, nil +} + +func (s *file) freeChunks(enc []byte) (err error) { + items, err := lldb.DecodeScalars(enc) + if err != nil { + return + } + + var ok bool + var next int64 + switch len(items) { + case 2: + return + case 3: + if next, ok = items[1].(int64); !ok || next == 0 { + return fmt.Errorf("(file-007) corrupted DB: first chunk link") + } + default: + return fmt.Errorf("(file-008) corrupted DB: first chunk") + } + + for next != 0 { + b, err := s.a.Get(nil, next) + if err != nil { + return err + } + + if items, err = lldb.DecodeScalars(b); err != nil { + return err + } + + var h int64 + switch len(items) { + case 1: + // nop + case 2: + if h, ok = items[0].(int64); !ok { + return fmt.Errorf("(file-009) corrupted DB: chunk link") + } + + default: + return fmt.Errorf("(file-010) corrupted DB: chunk items %d (%v)", len(items), items) + } + + s.mu.Lock() + if err = s.a.Free(next); err != nil { + s.mu.Unlock() + return err + } + + s.mu.Unlock() + next = h + } + return +} + +func (s *file) loadChunks(enc []byte) (v interface{}, err error) { + items, err := lldb.DecodeScalars(enc) + if err != nil { + return + } + + var ok bool + var next int64 + switch len(items) { + case 2: + // nop + case 3: + if next, ok = items[1].(int64); !ok || next == 0 { + return nil, fmt.Errorf("(file-011) corrupted DB: first chunk link") + } + default: + //fmt.Printf("%d: %#v\n", len(items), items) + return nil, fmt.Errorf("(file-012) corrupted DB: first chunk") + } + + typ, ok := items[0].(int64) + if !ok { + return nil, fmt.Errorf("(file-013) corrupted DB: first chunk tag") + } + + buf, ok := items[len(items)-1].([]byte) + if !ok { + return nil, fmt.Errorf("(file-014) corrupted DB: first chunk data") + } + + for next != 0 { + b, err := s.a.Get(nil, next) + if err != nil { + return nil, err + } + + if items, err = lldb.DecodeScalars(b); err != nil { + return nil, err + } + + switch len(items) { + case 1: + next = 0 + case 2: + if next, ok = items[0].(int64); !ok { + return nil, fmt.Errorf("(file-015) corrupted DB: chunk link") + } + + items = items[1:] + default: + return nil, fmt.Errorf("(file-016) corrupted DB: chunk items %d (%v)", len(items), items) + } + + if b, ok = items[0].([]byte); !ok { + return nil, fmt.Errorf("(file-017) corrupted DB: chunk data") + } + + buf = append(buf, b...) + } + return s.codec.decode(buf, int(typ)) +} + +func (s *file) Update(h int64, data ...interface{}) (err error) { + b, err := lldb.EncodeScalars(data...) + if err != nil { + return + } + + defer s.lock()() + return s.a.Realloc(h, b) +} + +func (s *file) UpdateRow(h int64, blobCols []*col, data ...interface{}) (err error) { + if len(blobCols) == 0 { + return s.Update(h, data...) + } + + if err = expand(data); err != nil { + return + } + + data0, err := s.Read(nil, h, blobCols...) + if err != nil { + return + } + + for _, c := range blobCols { + if c.index+2 >= len(data0) { + continue + } + + if x := data0[c.index+2]; x != nil { + if err = s.freeChunks(x.(chunk).b); err != nil { + return + } + } + } + + if err = s.flatten(data); err != nil { + return + } + + return s.Update(h, data...) +} + +// []interface{}{qltype, ...}->[]interface{}{lldb scalar type, ...} +// + long blobs are (pre)written to a chain of chunks. +func (s *file) flatten(data []interface{}) (err error) { + for i, v := range data { + tag := 0 + var b []byte + switch x := v.(type) { + case []byte: + tag = qBlob + b = x + case *big.Int: + tag = qBigInt + b, err = s.codec.encode(x) + case *big.Rat: + tag = qBigRat + b, err = s.codec.encode(x) + case time.Time: + tag = qTime + b, err = s.codec.encode(x) + case time.Duration: + tag = qDuration + b, err = s.codec.encode(x) + default: + continue + } + if err != nil { + return + } + + const chunk = 1 << 16 + chunks := 0 + var next int64 + var buf []byte + for rem := len(b); rem > shortBlob; { + n := mathutil.Min(rem, chunk) + part := b[rem-n:] + b = b[:rem-n] + rem -= n + switch next { + case 0: // last chunk + buf, err = lldb.EncodeScalars([]interface{}{part}...) + default: // middle chunk + buf, err = lldb.EncodeScalars([]interface{}{next, part}...) + } + if err != nil { + return + } + + s.mu.Lock() + h, err := s.a.Alloc(buf) + s.mu.Unlock() + if err != nil { + return err + } + + next = h + chunks++ + } + + switch next { + case 0: // single chunk + buf, err = lldb.EncodeScalars([]interface{}{tag, b}...) + default: // multi chunks + buf, err = lldb.EncodeScalars([]interface{}{tag, next, b}...) + } + if err != nil { + return + } + + data[i] = buf + } + return +} + +func lockName(dbname string) string { + base := filepath.Base(filepath.Clean(dbname)) + "lockfile" + h := sha1.New() + io.WriteString(h, base) + return filepath.Join(filepath.Dir(dbname), fmt.Sprintf(".%x", h.Sum(nil))) +} + +func walName(dbname string) (r string) { + base := filepath.Base(filepath.Clean(dbname)) + h := sha1.New() + io.WriteString(h, base) + return filepath.Join(filepath.Dir(dbname), fmt.Sprintf(".%x", h.Sum(nil))) +} + +type fileIndex struct { + f *file + h int64 + t *lldb.BTree + unique bool +} + +func (x *fileIndex) Clear() error { + return x.t.Clear() +} + +var gbZeroInt64 []byte + +func init() { + var err error + if gbZeroInt64, err = lldb.EncodeScalars(int64(0)); err != nil { + panic(err) + } +} + +func isIndexNull(data []interface{}) bool { + for _, v := range data { + if v != nil { + return false + } + } + return true +} + +// The []byte version of the key in the BTree shares chunks, if any, with +// the value stored in the record. +func (x *fileIndex) Create(indexedValues []interface{}, h int64) error { + for i, indexedValue := range indexedValues { + chunk, ok := indexedValue.(chunk) + if ok { + indexedValues[i] = chunk.b + } + } + + t := x.t + switch { + case !x.unique: + k, err := lldb.EncodeScalars(append(indexedValues, h)...) + if err != nil { + return err + } + + return t.Set(k, gbZeroInt64) + case isIndexNull(indexedValues): // unique, NULL + k, err := lldb.EncodeScalars(nil, h) + if err != nil { + return err + } + + return t.Set(k, gbZeroInt64) + default: // unique, non NULL + k, err := lldb.EncodeScalars(append(indexedValues, int64(0))...) + if err != nil { + return err + } + + v, err := lldb.EncodeScalars(h) + if err != nil { + return err + } + + _, _, err = t.Put(nil, k, func(key, old []byte) (new []byte, write bool, err error) { + if old == nil { + return v, true, nil + } + + return nil, false, fmt.Errorf("(file-018) cannot insert into unique index: duplicate value(s): %v", indexedValues) + }) + return err + } +} + +func (x *fileIndex) Delete(indexedValues []interface{}, h int64) error { + for i, indexedValue := range indexedValues { + chunk, ok := indexedValue.(chunk) + if ok { + indexedValues[i] = chunk.b + } + } + + t := x.t + var k []byte + var err error + switch { + case !x.unique: + k, err = lldb.EncodeScalars(append(indexedValues, h)...) + case isIndexNull(indexedValues): // unique, NULL + k, err = lldb.EncodeScalars(nil, h) + default: // unique, non NULL + k, err = lldb.EncodeScalars(append(indexedValues, int64(0))...) + } + if err != nil { + return err + } + + return t.Delete(k) +} + +func (x *fileIndex) Drop() error { + if err := x.Clear(); err != nil { + return err + } + + return x.f.a.Free(x.h) +} + +func (x *fileIndex) Seek(indexedValues []interface{}) (indexIterator, bool, error) { //TODO(indices) blobs: +test + k, err := lldb.EncodeScalars(append(indexedValues, 0)...) + if err != nil { + return nil, false, err + } + + en, hit, err := x.t.Seek(k) + if err != nil { + return nil, false, err + } + + return &fileIndexIterator{x.f, en, x.unique}, hit, nil +} + +func (x *fileIndex) SeekFirst() (iter indexIterator, err error) { + en, err := x.t.SeekFirst() + return &fileIndexIterator{x.f, en, x.unique}, err +} + +func (x *fileIndex) SeekLast() (iter indexIterator, err error) { + en, err := x.t.SeekLast() + return &fileIndexIterator{x.f, en, x.unique}, err +} + +type fileIndexIterator struct { + f *file + en *lldb.BTreeEnumerator + unique bool +} + +func (i *fileIndexIterator) nextPrev(f func() ([]byte, []byte, error)) ([]interface{}, int64, error) { //TODO(indices) blobs: +test + bk, bv, err := f() + if err != nil { + return nil, -1, err + } + + dk, err := lldb.DecodeScalars(bk) + if err != nil { + return nil, -1, err + } + + b, ok := dk[0].([]byte) + if ok { + dk[0] = chunk{i.f, b} + if expand(dk[:1]); err != nil { + return nil, -1, err + } + } + + var k indexKey + k.value = dk[:len(dk)-1] + switch i.unique { + case true: + if isIndexNull(k.value) { + return nil, dk[len(dk)-1].(int64), nil + } + + dv, err := lldb.DecodeScalars(bv) + if err != nil { + return nil, -1, err + } + + return k.value, dv[0].(int64), nil + default: + return k.value, dk[len(dk)-1].(int64), nil + } +} + +func (i *fileIndexIterator) Next() ([]interface{}, int64, error) { //TODO(indices) blobs: +test + return i.nextPrev(i.en.Next) +} + +func (i *fileIndexIterator) Prev() ([]interface{}, int64, error) { //TODO(indices) blobs: +test + return i.nextPrev(i.en.Prev) +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/helper.go b/Godeps/_workspace/src/github.com/cznic/ql/helper.go new file mode 100644 index 000000000..a21a46156 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/helper.go @@ -0,0 +1,338 @@ +// +build ignore + +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bufio" + "flag" + "fmt" + "io" + "log" + "os" +) + +type t int + +const ( + qNil t = iota + idealComplex + idealFloat + idealInt + idealRune + idealUint + qBool + qComplex64 + qComplex128 + qFloat32 + qFloat64 + qInt8 + qInt16 + qInt32 + qInt64 + qString + qUint8 + qUint16 + qUint32 + qUint64 + qBigInt + qBigRat + qTime + qDuration + + qEnd +) + +func (n t) String() string { + switch n { + case qNil: + return "nil" + case idealComplex: + return "idealComplex" + case idealFloat: + return "idealFloat" + case idealInt: + return "idealInt" + case idealRune: + return "idealRune" + case idealUint: + return "idealUint" + case qBool: + return "bool" + case qComplex64: + return "complex64" + case qComplex128: + return "complex128" + case qFloat32: + return "float32" + case qFloat64: + return "float64" + case qInt8: + return "int8" + case qInt16: + return "int16" + case qInt32: + return "int32" + case qInt64: + return "int64" + case qString: + return "string" + case qUint8: + return "uint8" + case qUint16: + return "uint16" + case qUint32: + return "uint32" + case qUint64: + return "uint64" + case qBigInt: + return "*big.Int" + case qBigRat: + return "*big.Rat" + case qTime: + return "time.Time" + case qDuration: + return "time.Duration" + default: + panic("internal error 046") + } +} + +func coerceIdealComplex(typ t) string { + switch typ { + case qComplex64, qComplex128: + return fmt.Sprintf("return %s(x)\n", typ) + default: + return "" + } +} + +func coerceIdealFloat(typ t) string { + switch typ { + case idealComplex: + return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ) + case qComplex64: + return fmt.Sprintf("return %s(complex(float32(x), 0))\n", typ) + case qComplex128: + return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ) + case idealFloat, qFloat32, qFloat64: + return fmt.Sprintf("return %s(float64(x))\n", typ) + case qBigRat: + return fmt.Sprintf("return big.NewRat(1, 1).SetFloat64(float64(x))\n") + default: + return "" + } + return "" +} + +func coerceIdealInt(typ t) string { + switch typ { + case idealComplex: + return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ) + case qComplex64: + return fmt.Sprintf("return %s(complex(float32(x), 0))\n", typ) + case qComplex128: + return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ) + case idealFloat, idealInt, qFloat32, qFloat64, qInt64: + return fmt.Sprintf("return %s(int64(x))\n", typ) + case idealUint: + return fmt.Sprintf("if x >= 0 { return %s(int64(x)) }\n", typ) + case qInt8: + return fmt.Sprintf("if x >= math.MinInt8 && x<= math.MaxInt8 { return %s(int64(x)) }\n", typ) + case qInt16: + return fmt.Sprintf("if x >= math.MinInt16 && x<= math.MaxInt16 { return %s(int64(x)) }\n", typ) + case qInt32: + return fmt.Sprintf("if x >= math.MinInt32 && x<= math.MaxInt32 { return %s(int64(x)) }\n", typ) + case qUint8: + return fmt.Sprintf("if x >= 0 && x<= math.MaxUint8 { return %s(int64(x)) }\n", typ) + case qUint16: + return fmt.Sprintf("if x >= 0 && x<= math.MaxUint16 { return %s(int64(x)) }\n", typ) + case qUint32: + return fmt.Sprintf("if x >= 0 && x<= math.MaxUint32 { return %s(int64(x)) }\n", typ) + case qUint64: + return fmt.Sprintf("if x >= 0 { return %s(int64(x)) }\n", typ) + case qBigInt: + return fmt.Sprintf("return big.NewInt(int64(x))\n") + case qBigRat: + return fmt.Sprintf("return big.NewRat(1, 1).SetInt64(int64(x))\n") + case qDuration: + return fmt.Sprintf("return time.Duration(int64(x))\n") + default: + return "" + } + return "" +} + +func coerceIdealRune(typ t) string { + switch typ { + case idealComplex: + return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ) + case qComplex64: + return fmt.Sprintf("return %s(complex(float32(x), 0))\n", typ) + case qComplex128: + return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ) + case idealFloat, idealInt, idealRune, idealUint, qFloat32, qFloat64, qInt8, qInt16, qInt32, qInt64, qUint8, qUint16, qUint32, qUint64: + return fmt.Sprintf("return %s(int64(x))\n", typ) + case qBigInt: + return fmt.Sprintf("return big.NewInt(int64(x))\n") + case qBigRat: + return fmt.Sprintf("return big.NewRat(1, 1).SetInt64(int64(x))\n") + case qDuration: + return fmt.Sprintf("return time.Duration(int64(x))\n") + default: + return "" + } + return "" +} + +func coerceIdealUint(typ t) string { + switch typ { + case idealComplex: + return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ) + case qComplex64: + return fmt.Sprintf("return %s(complex(float32(x), 0))\n", typ) + case qComplex128: + return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ) + case idealFloat, idealUint, qFloat32, qFloat64, qUint64: + return fmt.Sprintf("return %s(uint64(x))\n", typ) + case idealInt: + return fmt.Sprintf("if x <= math.MaxInt64 { return %s(int64(x)) }\n", typ) + case qInt8: + return fmt.Sprintf("if x <= math.MaxInt8 { return %s(int64(x)) }\n", typ) + case qInt16: + return fmt.Sprintf("if x<= math.MaxInt16 { return %s(int64(x)) }\n", typ) + case qInt32: + return fmt.Sprintf("if x<= math.MaxInt32 { return %s(int64(x)) }\n", typ) + case qInt64: + return fmt.Sprintf("if x<= math.MaxInt64 { return %s(int64(x)) }\n", typ) + case qUint8: + return fmt.Sprintf("if x >= 0 && x<= math.MaxUint8 { return %s(int64(x)) }\n", typ) + case qUint16: + return fmt.Sprintf("if x >= 0 && x<= math.MaxUint16 { return %s(int64(x)) }\n", typ) + case qUint32: + return fmt.Sprintf("if x >= 0 && x<= math.MaxUint32 { return %s(int64(x)) }\n", typ) + case qBigInt: + return fmt.Sprintf("return big.NewInt(0).SetUint64(uint64(x))\n") + case qBigRat: + return fmt.Sprintf("return big.NewRat(1, 1).SetInt(big.NewInt(0).SetUint64(uint64(x)))\n") + case qDuration: + return fmt.Sprintf("if x <= math.MaxInt64 { return time.Duration(int64(x)) }\n") + default: + return "" + } + return "" +} + +func genCoerce1(w io.Writer, in t, f func(out t) string) { + fmt.Fprintf(w, "\tcase %s:\n", in) + fmt.Fprintf(w, "\t\tswitch otherVal.(type) {\n") + + for i := idealComplex; i < qEnd; i++ { + s := f(i) + switch s { + case "": + fmt.Fprintf(w, "\t\t//case %s:\n", i) + default: + fmt.Fprintf(w, "\t\tcase %s:\n", i) + fmt.Fprintf(w, "\t\t\t%s", s) + } + } + + fmt.Fprintf(w, "\t\t}\n") // switch +} + +func genCoerce(w io.Writer) { + fmt.Fprintf(w, + ` +func coerce1(inVal, otherVal interface{}) (coercedInVal interface{}) { + coercedInVal = inVal + if otherVal == nil { + return + } + + switch x := inVal.(type) { + case nil: + return +`) + genCoerce1(w, idealComplex, coerceIdealComplex) + genCoerce1(w, idealFloat, coerceIdealFloat) + genCoerce1(w, idealInt, coerceIdealInt) + genCoerce1(w, idealRune, coerceIdealRune) + genCoerce1(w, idealUint, coerceIdealUint) + fmt.Fprintf(w, "\t}\n") // switch + + fmt.Fprintf(w, "\treturn\n}\n") // func +} + +func main() { + ofn := flag.String("o", "", "") + flag.Parse() + _, err := os.Stat(*ofn) + if err == nil { + log.Fatalf("%s exists", *ofn) + } + + w := bufio.NewWriter(os.Stdout) + if s := *ofn; s != "" { + f, err := os.Create(s) + if err != nil { + log.Fatal(err) + } + + defer f.Close() + w = bufio.NewWriter(f) + } + defer w.Flush() + + fmt.Fprintf(w, `// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// CAUTION: This file was generated automatically by +// +// $ go run helper -o coerce.go +// +// DO NOT EDIT! + + package ql + +import ( + "math" + "math/big" + "reflect" + "time" +) + +func coerce(a, b interface{}) (x, y interface{}) { + if reflect.TypeOf(a) == reflect.TypeOf(b) { + return a, b + } + + switch a.(type) { + case idealComplex, idealFloat, idealInt, idealRune, idealUint: + switch b.(type) { + case idealComplex, idealFloat, idealInt, idealRune, idealUint: + x, y = coerce1(a, b), b + if reflect.TypeOf(x) == reflect.TypeOf(y) { + return + } + + return a, coerce1(b, a) + default: + return coerce1(a, b), b + } + default: + switch b.(type) { + case idealComplex, idealFloat, idealInt, idealRune, idealUint: + return a, coerce1(b, a) + default: + return a, b + } + } +} +`) + genCoerce(w) +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/httpfs.go b/Godeps/_workspace/src/github.com/cznic/ql/httpfs.go new file mode 100644 index 000000000..89c6f89c5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/httpfs.go @@ -0,0 +1,302 @@ +// Copyright (c) 2014 ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "fmt" + "io" + "net/http" + "os" + "path" + "path/filepath" + "strings" + "time" + + "github.com/cznic/mathutil" +) + +var ( + _ http.FileSystem = (*HTTPFS)(nil) + _ http.File = (*HTTPFile)(nil) + _ os.FileInfo = (*HTTPFile)(nil) + _ os.FileInfo = (*dirEntry)(nil) +) + +type dirEntry string + +func (d dirEntry) Name() string { return string(d) } +func (d dirEntry) Size() int64 { return -1 } +func (d dirEntry) Mode() os.FileMode { return os.ModeDir } +func (d dirEntry) ModTime() time.Time { return time.Time{} } +func (d dirEntry) IsDir() bool { return true } +func (d dirEntry) Sys() interface{} { return interface{}(nil) } + +// A HTTPFile is returned by the HTTPFS's Open method and can be served by the +// http.FileServer implementation. +type HTTPFile struct { + closed bool + content []byte + dirEntries []os.FileInfo + isFile bool + name string + off int + sz int +} + +// Close implements http.File. +func (f *HTTPFile) Close() error { + if f.closed { + return os.ErrInvalid + } + + f.closed = true + return nil +} + +// IsDir implements os.FileInfo +func (f *HTTPFile) IsDir() bool { return !f.isFile } + +// Mode implements os.FileInfo +func (f *HTTPFile) Mode() os.FileMode { + switch f.isFile { + case false: + return os.FileMode(0444) + default: + return os.ModeDir + } +} + +// ModTime implements os.FileInfo +func (f *HTTPFile) ModTime() time.Time { + return time.Time{} +} + +// Name implements os.FileInfo +func (f *HTTPFile) Name() string { return path.Base(f.name) } + +// Size implements os.FileInfo +func (f *HTTPFile) Size() int64 { + switch f.isFile { + case false: + return -1 + default: + return int64(len(f.content)) + } +} + +// Stat implements http.File. +func (f *HTTPFile) Stat() (os.FileInfo, error) { return f, nil } + +// Sys implements os.FileInfo +func (f *HTTPFile) Sys() interface{} { return interface{}(nil) } + +// Readdir implements http.File. +func (f *HTTPFile) Readdir(count int) ([]os.FileInfo, error) { + if f.isFile { + return nil, fmt.Errorf("not a directory: %s", f.name) + } + + if count <= 0 { + r := f.dirEntries + f.dirEntries = f.dirEntries[:0] + return r, nil + } + + rq := mathutil.Min(count, len(f.dirEntries)) + r := f.dirEntries[:rq] + f.dirEntries = f.dirEntries[rq:] + if len(r) != 0 { + return r, nil + } + + return nil, io.EOF +} + +// Read implements http.File. +func (f *HTTPFile) Read(b []byte) (int, error) { + if f.closed { + return 0, os.ErrInvalid + } + + n := copy(b, f.content[f.off:]) + f.off += n + if n != 0 { + return n, nil + } + + return 0, io.EOF +} + +// Seek implements http.File. +func (f *HTTPFile) Seek(offset int64, whence int) (int64, error) { + if f.closed { + return 0, os.ErrInvalid + } + + if offset < 0 { + return int64(f.off), fmt.Errorf("cannot seek before start of file") + } + + switch whence { + case 0: + noff := int64(f.off) + offset + if noff > mathutil.MaxInt { + return int64(f.off), fmt.Errorf("seek target overflows int: %d", noff) + } + + f.off = mathutil.Min(int(offset), len(f.content)) + if f.off == int(offset) { + return offset, nil + } + + return int64(f.off), io.EOF + case 1: + noff := int64(f.off) + offset + if noff > mathutil.MaxInt { + return int64(f.off), fmt.Errorf("seek target overflows int: %d", noff) + } + + off := mathutil.Min(f.off+int(offset), len(f.content)) + if off == f.off+int(offset) { + f.off = off + return int64(off), nil + } + + f.off = off + return int64(off), io.EOF + case 2: + noff := int64(f.off) - offset + if noff < 0 { + return int64(f.off), fmt.Errorf("cannot seek before start of file") + } + + f.off = len(f.content) - int(offset) + return int64(f.off), nil + default: + return int64(f.off), fmt.Errorf("seek: invalid whence %d", whence) + } +} + +// HTTPFS implements a http.FileSystem backed by data in a DB. +type HTTPFS struct { + db *DB + dir, get List +} + +// NewHTTPFS returns a http.FileSystem backed by a result record set of query. +// The record set provides two mandatory fields: path and content (the field +// names are case sensitive). Type of name must be string and type of content +// must be blob (ie. []byte). Field 'path' value is the "file" pathname, which +// must be rooted; and field 'content' value is its "data". +func (db *DB) NewHTTPFS(query string) (*HTTPFS, error) { + if _, err := Compile(query); err != nil { + return nil, err + } + + dir, err := Compile(fmt.Sprintf("SELECT path FROM (%s) WHERE hasPrefix(path, $1)", query)) + if err != nil { + return nil, err + } + + get, err := Compile(fmt.Sprintf("SELECT content FROM (%s) WHERE path == $1", query)) + if err != nil { + return nil, err + } + + return &HTTPFS{db: db, dir: dir, get: get}, nil +} + +// Open implements http.FileSystem. The name parameter represents a file path. +// The elements in a file path are separated by slash ('/', U+002F) characters, +// regardless of host operating system convention. +func (f *HTTPFS) Open(name string) (http.File, error) { + if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 || + strings.Contains(name, "\x00") { + return nil, fmt.Errorf("invalid character in file path: %q", name) + } + + name = path.Clean("/" + name) + rs, _, err := f.db.Execute(nil, f.get, name) + if err != nil { + return nil, err + } + + n := 0 + var fdata []byte + if err = rs[0].Do(false, func(data []interface{}) (more bool, err error) { + switch n { + case 0: + var ok bool + fdata, ok = data[0].([]byte) + if !ok { + return false, fmt.Errorf("open: expected blob, got %T", data[0]) + } + n++ + return true, nil + default: + return false, fmt.Errorf("open: more than one result was returned for %s", name) + } + }); err != nil { + return nil, err + } + + if n == 1 { // file found + return &HTTPFile{name: name, isFile: true, content: fdata}, nil + } + + dirName := name + if dirName[len(dirName)-1] != filepath.Separator { + dirName += string(filepath.Separator) + } + // Open("/a/b"): {/a/b/c.x,/a/b/d.x,/a/e.x,/a/b/f/g.x} -> {c.x,d.x,f} + rs, _, err = f.db.Execute(nil, f.dir, dirName) + if err != nil { + return nil, err + } + + n = 0 + r := &HTTPFile{name: dirName} + m := map[string]bool{} + x := len(dirName) + if err = rs[0].Do(false, func(data []interface{}) (more bool, err error) { + n++ + switch name := data[0].(type) { + case string: + if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 || + strings.Contains(name, "\x00") { + return false, fmt.Errorf("invalid character in file path: %q", name) + } + + name = path.Clean("/" + name) + rest := name[x:] + parts := strings.Split(rest, "/") + if len(parts) == 0 { + return true, nil + } + + nm := parts[0] + switch len(parts) { + case 1: // file + r.dirEntries = append(r.dirEntries, &HTTPFile{isFile: true, name: nm}) + default: // directory + if !m[nm] { + r.dirEntries = append(r.dirEntries, dirEntry(nm)) + } + m[nm] = true + } + return true, nil + default: + return false, fmt.Errorf("expected string path, got %T(%v)", name, name) + } + }); err != nil { + return nil, err + } + + if n != 0 { + return r, nil + } + + return nil, os.ErrNotExist +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/httpfs_test.go b/Godeps/_workspace/src/github.com/cznic/ql/httpfs_test.go new file mode 100644 index 000000000..1ae79d305 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/httpfs_test.go @@ -0,0 +1,547 @@ +// Copyright (c) 2014 ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "io" + "os" + "path" + "testing" +) + +func TestHTTP(t *testing.T) { + db, err := OpenMem() + if err != nil { + t.Fatal(err) + } + if _, _, err = db.Run( + NewRWCtx(), + ` + BEGIN TRANSACTION; + CREATE TABLE t (path string); + INSERT INTO t VALUES + ("/a/b/c/1"), + ("/a/b/c/2"), + ("/a/b/3"), + ("/a/b/4"), + ("/a/5"), + ("/a/6"), + ; + COMMIT; + `, + ); err != nil { + t.Fatal(err) + } + + fs, err := db.NewHTTPFS("SELECT path, blob(path+`-c`) AS content FROM t") + if err != nil { + t.Fatal(err) + } + + for _, nm := range []string{"/a/b/c/1", "/a/b/4", "/a/5"} { + f, err := fs.Open(nm) + if err != nil { + t.Fatal(err) + } + + stat, err := f.Stat() + if err != nil { + t.Fatal(err) + } + + b := make([]byte, 100) + n, err := f.Read(b) + if err != nil { + t.Fatal(nm, n, err) + } + + g := string(b[:n]) + if e := nm + "-c"; g != e { + t.Fatal(g, e) + } + + if g, e := stat.Name(), path.Base(nm); g != e { + t.Fatal(g, e) + } + + if g, e := stat.Size(), int64(len(g)); g != e { + t.Fatal(g, e) + } + + if g, e := stat.IsDir(), false; g != e { + t.Fatal(g, e) + } + + b = make([]byte, 100) + n, err = f.Read(b) + if n != 0 { + t.Error(n) + } + + if err != io.EOF { + t.Fatal(err) + } + + if n, err := f.Seek(0, 0); err != nil || n != 0 { + t.Fatal(n, err) + } + + exp := []byte(nm + "-c") + b = make([]byte, 1) + for _, e := range exp { + n, err := f.Read(b) + if n != 1 || err != nil { + t.Fatal(n, err) + } + + if g := b[0]; g != e { + t.Fatal(g, e) + } + } + if n, err := f.Read(b); n != 0 || err != io.EOF { + t.Fatal(n, err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + + if n, err := f.Seek(0, 0); err != os.ErrInvalid { + t.Fatal(n, err) + } + } + + if _, err = fs.Open("nonexistent"); err != os.ErrNotExist { + t.Fatal(err) + } + + // ------------------------------------------------------------------ / + d, err := fs.Open("") + if err != nil { + t.Fatal(err) + } + + stat, err := d.Stat() + if err != nil { + t.Fatal(err) + } + + if g, e := stat.IsDir(), true; g != e { + t.Fatal(g, e) + } + + list, err := d.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if g, e := len(list), 1; g != e { + t.Fatal(g, e) + } + + var a bool + for _, v := range list { + switch v.IsDir() { + case true: + switch v.Name() { + case "a": + a = true + default: + t.Fatal(v.Name()) + } + default: + t.Fatal(v.IsDir()) + } + } + if !a { + t.Fatal(a) + } + + // ------------------------------------------------------------------ a + d, err = fs.Open("a") + if err != nil { + t.Fatal(err) + } + + stat, err = d.Stat() + if err != nil { + t.Fatal(err) + } + + if g, e := stat.IsDir(), true; g != e { + t.Fatal(g, e) + } + + list, err = d.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if g, e := len(list), 3; g != e { + t.Fatal(g, e) + } + + var aB, a5, a6 bool + for _, v := range list { + switch v.IsDir() { + case true: + switch v.Name() { + case "b": + aB = true + default: + t.Fatal(v.Name()) + } + default: + switch v.Name() { + case "5": + a5 = true + case "6": + a6 = true + default: + t.Fatal(v.Name()) + } + } + } + if !(aB && a5 && a6) { + t.Fatal(aB, a5, a6) + } + + // ----------------------------------------------------------------- a/ + d, err = fs.Open("a/") + if err != nil { + t.Fatal(err) + } + + stat, err = d.Stat() + if err != nil { + t.Fatal(err) + } + + if g, e := stat.IsDir(), true; g != e { + t.Fatal(g, e) + } + + list, err = d.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if g, e := len(list), 3; g != e { + t.Fatal(g, e) + } + + for _, v := range list { + switch v.IsDir() { + case true: + switch v.Name() { + case "b": + aB = true + default: + t.Fatal(v.Name()) + } + default: + switch v.Name() { + case "5": + a5 = true + case "6": + a6 = true + default: + t.Fatal(v.Name()) + } + } + } + if !(aB && a5 && a6) { + t.Fatal(aB, a5, a6) + } + + // ----------------------------------------------------------------- /a + d, err = fs.Open("/a") + if err != nil { + t.Fatal(err) + } + + stat, err = d.Stat() + if err != nil { + t.Fatal(err) + } + + if g, e := stat.IsDir(), true; g != e { + t.Fatal(g, e) + } + + list, err = d.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if g, e := len(list), 3; g != e { + t.Fatal(g, e) + } + + for _, v := range list { + switch v.IsDir() { + case true: + switch v.Name() { + case "b": + aB = true + default: + t.Fatal(v.Name()) + } + default: + switch v.Name() { + case "5": + a5 = true + case "6": + a6 = true + default: + t.Fatal(v.Name()) + } + } + } + if !(aB && a5 && a6) { + t.Fatal(aB, a5, a6) + } + + // ---------------------------------------------------------------- /a/ + d, err = fs.Open("/a/") + if err != nil { + t.Fatal(err) + } + + stat, err = d.Stat() + if err != nil { + t.Fatal(err) + } + + if g, e := stat.IsDir(), true; g != e { + t.Fatal(g, e) + } + + list, err = d.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if g, e := len(list), 3; g != e { + t.Fatal(g, e) + } + + for _, v := range list { + switch v.IsDir() { + case true: + switch v.Name() { + case "b": + aB = true + default: + t.Fatal(v.Name()) + } + default: + switch v.Name() { + case "5": + a5 = true + case "6": + a6 = true + default: + t.Fatal(v.Name()) + } + } + } + if !(aB && a5 && a6) { + t.Fatal(aB, a5, a6) + } + + // ---------------------------------------------------------------- a/b + d, err = fs.Open("a/b") + if err != nil { + t.Fatal(err) + } + + stat, err = d.Stat() + if err != nil { + t.Fatal(err) + } + + if g, e := stat.IsDir(), true; g != e { + t.Fatal(g, e) + } + + list, err = d.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if g, e := len(list), 3; g != e { + t.Fatal(g, e) + } + + var aB3, aB4, aBC bool + for _, v := range list { + switch v.IsDir() { + case true: + switch v.Name() { + case "c": + aBC = true + default: + t.Fatal(v.Name()) + } + default: + switch v.Name() { + case "3": + aB3 = true + case "4": + aB4 = true + default: + t.Fatal(v.Name()) + } + } + } + if !(aB3 && aB4 && aBC) { + t.Fatal(aB4, aB4, aBC) + } + + // --------------------------------------------------------------- a/b/ + d, err = fs.Open("a/b/") + if err != nil { + t.Fatal(err) + } + + stat, err = d.Stat() + if err != nil { + t.Fatal(err) + } + + if g, e := stat.IsDir(), true; g != e { + t.Fatal(g, e) + } + + list, err = d.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if g, e := len(list), 3; g != e { + t.Fatal(g, e) + } + + for _, v := range list { + switch v.IsDir() { + case true: + switch v.Name() { + case "c": + aBC = true + default: + t.Fatal(v.Name()) + } + default: + switch v.Name() { + case "3": + aB3 = true + case "4": + aB4 = true + default: + t.Fatal(v.Name()) + } + } + } + if !(aB3 && aB4 && aBC) { + t.Fatal(aB4, aB4, aBC) + } + + // -------------------------------------------------------------- /a/b/ + d, err = fs.Open("/a/b/") + if err != nil { + t.Fatal(err) + } + + stat, err = d.Stat() + if err != nil { + t.Fatal(err) + } + + if g, e := stat.IsDir(), true; g != e { + t.Fatal(g, e) + } + + list, err = d.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if g, e := len(list), 3; g != e { + t.Fatal(g, e) + } + + for _, v := range list { + switch v.IsDir() { + case true: + switch v.Name() { + case "c": + aBC = true + default: + t.Fatal(v.Name()) + } + default: + switch v.Name() { + case "3": + aB3 = true + case "4": + aB4 = true + default: + t.Fatal(v.Name()) + } + } + } + if !(aB3 && aB4 && aBC) { + t.Fatal(aB4, aB4, aBC) + } + + // --------------------------------------------------------------- /a/b + d, err = fs.Open("/a/b") + if err != nil { + t.Fatal(err) + } + + stat, err = d.Stat() + if err != nil { + t.Fatal(err) + } + + if g, e := stat.IsDir(), true; g != e { + t.Fatal(g, e) + } + + list, err = d.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if g, e := len(list), 3; g != e { + t.Fatal(g, e) + } + + for _, v := range list { + switch v.IsDir() { + case true: + switch v.Name() { + case "c": + aBC = true + default: + t.Fatal(v.Name()) + } + default: + switch v.Name() { + case "3": + aB3 = true + case "4": + aB4 = true + default: + t.Fatal(v.Name()) + } + } + } + if !(aB3 && aB4 && aBC) { + t.Fatal(aB4, aB4, aBC) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/introspection.go b/Godeps/_workspace/src/github.com/cznic/ql/introspection.go new file mode 100644 index 000000000..61ac9a928 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/introspection.go @@ -0,0 +1,625 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "bytes" + "fmt" + "go/ast" + "reflect" + "strings" + "sync" +) + +var ( + schemaCache = map[reflect.Type]*StructInfo{} + schemaMu sync.RWMutex +) + +// StructInfo describes a struct type. An instance of StructInfo obtained from +// StructSchema is shared and must not be mutated. That includes the values +// pointed to by the elements of Fields and Indices. +type StructInfo struct { + Fields []*StructField // Fields describe the considered fields of a struct type. + HasID bool // Whether the struct has a considered field named ID of type int64. + Indices []*StructIndex // Indices describe indices defined by the index or uindex ql tags. + IsPtr bool // Whether the StructInfo was derived from a pointer to a struct. +} + +// StructIndex describes an index defined by the ql tag index or uindex. +type StructIndex struct { + ColumnName string // Name of the column the index is on. + Name string // Name of the index. + Unique bool // Whether the index is unique. +} + +// StructField describes a considered field of a struct type. +type StructField struct { + Index int // Index is the index of the field for reflect.Value.Field. + IsID bool // Whether the field corresponds to record id(). + IsPtr bool // Whether the field is a pointer type. + MarshalType reflect.Type // The reflect.Type a field must be converted to when marshaling or nil when it is assignable directly. (Field->value) + Name string // Field name or value of the name tag (like in `ql:"name foo"`). + ReflectType reflect.Type // The reflect.Type of the field. + Tags map[string]string // QL tags of this field. (`ql:"a, b c, d"` -> {"a": "", "b": "c", "d": ""}) + Type Type // QL type of the field. + UnmarshalType reflect.Type // The reflect.Type a value must be converted to when unmarshaling or nil when it is assignable directly. (Field<-value) + ZeroPtr reflect.Value // The reflect.Zero value of the field if it's a pointer type. +} + +func (s *StructField) check(v interface{}) error { + t := reflect.TypeOf(v) + if !s.ReflectType.AssignableTo(t) { + if !s.ReflectType.ConvertibleTo(t) { + return fmt.Errorf("type %s (%v) cannot be converted to %T", s.ReflectType.Name(), s.ReflectType.Kind(), t.Name()) + } + + s.MarshalType = t + } + + if !t.AssignableTo(s.ReflectType) { + if !t.ConvertibleTo(s.ReflectType) { + return fmt.Errorf("type %s (%v) cannot be converted to %T", t.Name(), t.Kind(), s.ReflectType.Name()) + } + + s.UnmarshalType = s.ReflectType + } + return nil +} + +func parseTag(s string) map[string]string { + m := map[string]string{} + for _, v := range strings.Split(s, ",") { + v = strings.TrimSpace(v) + switch n := strings.IndexRune(v, ' '); { + case n < 0: + m[v] = "" + default: + m[v[:n]] = v[n+1:] + } + } + return m +} + +// StructSchema returns StructInfo for v which must be a struct instance or a +// pointer to a struct. The info is computed only once for every type. +// Subsequent calls to StructSchema for the same type return a cached +// StructInfo. +// +// Note: The returned StructSchema is shared and must be not mutated, including +// any other data structures it may point to. +func StructSchema(v interface{}) (*StructInfo, error) { + if v == nil { + return nil, fmt.Errorf("cannot derive schema for %T(%v)", v, v) + } + + typ := reflect.TypeOf(v) + schemaMu.RLock() + if r, ok := schemaCache[typ]; ok { + schemaMu.RUnlock() + return r, nil + } + + schemaMu.RUnlock() + var schemaPtr bool + t := typ + if t.Kind() == reflect.Ptr { + t = t.Elem() + schemaPtr = true + } + if k := t.Kind(); k != reflect.Struct { + return nil, fmt.Errorf("cannot derive schema for type %T (%v)", v, k) + } + + r := &StructInfo{IsPtr: schemaPtr} + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + fn := f.Name + if !ast.IsExported(fn) { + continue + } + + tags := parseTag(f.Tag.Get("ql")) + if _, ok := tags["-"]; ok { + continue + } + + if s := tags["name"]; s != "" { + fn = s + } + + if fn == "ID" && f.Type.Kind() == reflect.Int64 { + r.HasID = true + } + var ix, unique bool + var xn string + xfn := fn + if s := tags["index"]; s != "" { + if _, ok := tags["uindex"]; ok { + return nil, fmt.Errorf("both index and uindex in QL struct tag") + } + + ix, xn = true, s + } else if s := tags["uindex"]; s != "" { + if _, ok := tags["index"]; ok { + return nil, fmt.Errorf("both index and uindex in QL struct tag") + } + + ix, unique, xn = true, true, s + } + if ix { + if fn == "ID" && r.HasID { + xfn = "id()" + } + r.Indices = append(r.Indices, &StructIndex{Name: xn, ColumnName: xfn, Unique: unique}) + } + + sf := &StructField{Index: i, Name: fn, Tags: tags, Type: Type(-1), ReflectType: f.Type} + fk := sf.ReflectType.Kind() + if fk == reflect.Ptr { + sf.IsPtr = true + sf.ZeroPtr = reflect.Zero(sf.ReflectType) + sf.ReflectType = sf.ReflectType.Elem() + fk = sf.ReflectType.Kind() + } + + switch fk { + case reflect.Bool: + sf.Type = Bool + if err := sf.check(false); err != nil { + return nil, err + } + case reflect.Int, reflect.Uint: + return nil, fmt.Errorf("only integers of fixed size can be used to derive a schema: %v", fk) + case reflect.Int8: + sf.Type = Int8 + if err := sf.check(int8(0)); err != nil { + return nil, err + } + case reflect.Int16: + if err := sf.check(int16(0)); err != nil { + return nil, err + } + sf.Type = Int16 + case reflect.Int32: + if err := sf.check(int32(0)); err != nil { + return nil, err + } + sf.Type = Int32 + case reflect.Int64: + if sf.ReflectType.Name() == "Duration" && sf.ReflectType.PkgPath() == "time" { + sf.Type = Duration + break + } + + sf.Type = Int64 + if err := sf.check(int64(0)); err != nil { + return nil, err + } + case reflect.Uint8: + sf.Type = Uint8 + if err := sf.check(uint8(0)); err != nil { + return nil, err + } + case reflect.Uint16: + sf.Type = Uint16 + if err := sf.check(uint16(0)); err != nil { + return nil, err + } + case reflect.Uint32: + sf.Type = Uint32 + if err := sf.check(uint32(0)); err != nil { + return nil, err + } + case reflect.Uint64: + sf.Type = Uint64 + if err := sf.check(uint64(0)); err != nil { + return nil, err + } + case reflect.Float32: + sf.Type = Float32 + if err := sf.check(float32(0)); err != nil { + return nil, err + } + case reflect.Float64: + sf.Type = Float64 + if err := sf.check(float64(0)); err != nil { + return nil, err + } + case reflect.Complex64: + sf.Type = Complex64 + if err := sf.check(complex64(0)); err != nil { + return nil, err + } + case reflect.Complex128: + sf.Type = Complex128 + if err := sf.check(complex128(0)); err != nil { + return nil, err + } + case reflect.Slice: + sf.Type = Blob + if err := sf.check([]byte(nil)); err != nil { + return nil, err + } + case reflect.Struct: + switch sf.ReflectType.PkgPath() { + case "math/big": + switch sf.ReflectType.Name() { + case "Int": + sf.Type = BigInt + case "Rat": + sf.Type = BigRat + } + case "time": + switch sf.ReflectType.Name() { + case "Time": + sf.Type = Time + } + } + case reflect.String: + sf.Type = String + if err := sf.check(""); err != nil { + return nil, err + } + } + + if sf.Type < 0 { + return nil, fmt.Errorf("cannot derive schema for type %s (%v)", sf.ReflectType.Name(), fk) + } + + sf.IsID = fn == "ID" && r.HasID + r.Fields = append(r.Fields, sf) + } + + schemaMu.Lock() + schemaCache[typ] = r + if t != typ { + r2 := *r + r2.IsPtr = false + schemaCache[t] = &r2 + } + schemaMu.Unlock() + return r, nil +} + +// MustStructSchema is like StructSchema but panics on error. It simplifies +// safe initialization of global variables holding StructInfo. +// +// MustStructSchema is safe for concurrent use by multiple goroutines. +func MustStructSchema(v interface{}) *StructInfo { + s, err := StructSchema(v) + if err != nil { + panic(err) + } + + return s +} + +// SchemaOptions amend the result of Schema. +type SchemaOptions struct { + // Don't wrap the CREATE statement(s) in a transaction. + NoTransaction bool + + // Don't insert the IF NOT EXISTS clause in the CREATE statement(s). + NoIfNotExists bool + + // Do not strip the "pkg." part from type name "pkg.Type", produce + // "pkg_Type" table name instead. Applies only when no name is passed + // to Schema(). + KeepPrefix bool +} + +var zeroSchemaOptions SchemaOptions + +// Schema returns a CREATE TABLE/INDEX statement(s) for a table derived from a +// struct or an error, if any. The table is named using the name parameter. If +// name is an empty string then the type name of the struct is used while non +// conforming characters are replaced by underscores. Value v can be also a +// pointer to a struct. +// +// Every considered struct field type must be one of the QL types or a type +// convertible to string, bool, int*, uint*, float* or complex* type or pointer +// to such type. Integers with a width dependent on the architecture can not be +// used. Only exported fields are considered. If an exported field QL tag +// contains "-" (`ql:"-"`) then such field is not considered. A field with name +// ID, having type int64, corresponds to id() - and is thus not a part of the +// CREATE statement. A field QL tag containing "index name" or "uindex name" +// triggers additionally creating an index or unique index on the respective +// field. Fields can be renamed using a QL tag "name newName". Fields are +// considered in the order of appearance. A QL tag is a struct tag part +// prefixed by "ql:". Tags can be combined, for example: +// +// type T struct { +// Foo string `ql:"index xFoo, name Bar"` +// } +// +// If opts.NoTransaction == true then the statement(s) are not wrapped in a +// transaction. If opt.NoIfNotExists == true then the CREATE statement(s) omits +// the IF NOT EXISTS clause. Passing nil opts is equal to passing +// &SchemaOptions{} +// +// Schema is safe for concurrent use by multiple goroutines. +func Schema(v interface{}, name string, opt *SchemaOptions) (List, error) { + if opt == nil { + opt = &zeroSchemaOptions + } + s, err := StructSchema(v) + if err != nil { + return List{}, err + } + + var buf bytes.Buffer + if !opt.NoTransaction { + buf.WriteString("BEGIN TRANSACTION; ") + } + buf.WriteString("CREATE TABLE ") + if !opt.NoIfNotExists { + buf.WriteString("IF NOT EXISTS ") + } + if name == "" { + name = fmt.Sprintf("%T", v) + if !opt.KeepPrefix { + a := strings.Split(name, ".") + if l := len(a); l > 1 { + name = a[l-1] + } + } + nm := []rune{} + for _, v := range name { + switch { + case v >= '0' && v <= '9' || v == '_' || v >= 'a' && v <= 'z' || v >= 'A' && v <= 'Z': + // ok + default: + v = '_' + } + nm = append(nm, v) + } + name = string(nm) + } + buf.WriteString(name + " (") + for _, v := range s.Fields { + if v.IsID { + continue + } + + buf.WriteString(fmt.Sprintf("%s %s, ", v.Name, v.Type)) + } + buf.WriteString("); ") + for _, v := range s.Indices { + buf.WriteString("CREATE ") + if v.Unique { + buf.WriteString("UNIQUE ") + } + buf.WriteString("INDEX ") + if !opt.NoIfNotExists { + buf.WriteString("IF NOT EXISTS ") + } + buf.WriteString(fmt.Sprintf("%s ON %s (%s); ", v.Name, name, v.ColumnName)) + } + if !opt.NoTransaction { + buf.WriteString("COMMIT; ") + } + l, err := Compile(buf.String()) + if err != nil { + return List{}, fmt.Errorf("%s: %v", buf.String(), err) + } + + return l, nil +} + +// MustSchema is like Schema but panics on error. It simplifies safe +// initialization of global variables holding compiled schemas. +// +// MustSchema is safe for concurrent use by multiple goroutines. +func MustSchema(v interface{}, name string, opt *SchemaOptions) List { + l, err := Schema(v, name, opt) + if err != nil { + panic(err) + } + + return l +} + +// Marshal converts, in the order of appearance, fields of a struct instance v +// to []interface{} or an error, if any. Value v can be also a pointer to a +// struct. +// +// Every considered struct field type must be one of the QL types or a type +// convertible to string, bool, int*, uint*, float* or complex* type or pointer +// to such type. Integers with a width dependent on the architecture can not be +// used. Only exported fields are considered. If an exported field QL tag +// contains "-" then such field is not considered. A QL tag is a struct tag +// part prefixed by "ql:". Field with name ID, having type int64, corresponds +// to id() - and is thus not part of the result. +// +// Marshal is safe for concurrent use by multiple goroutines. +func Marshal(v interface{}) ([]interface{}, error) { + s, err := StructSchema(v) + if err != nil { + return nil, err + } + + val := reflect.ValueOf(v) + if s.IsPtr { + val = val.Elem() + } + n := len(s.Fields) + if s.HasID { + n-- + } + r := make([]interface{}, n) + j := 0 + for _, v := range s.Fields { + if v.IsID { + continue + } + + f := val.Field(v.Index) + if v.IsPtr { + if f.IsNil() { + r[j] = nil + j++ + continue + } + + f = f.Elem() + } + if m := v.MarshalType; m != nil { + f = f.Convert(m) + } + r[j] = f.Interface() + j++ + } + return r, nil +} + +// MustMarshal is like Marshal but panics on error. It simplifies marshaling of +// "safe" types, like eg. those which were already verified by Schema or +// MustSchema. When the underlying Marshal returns an error, MustMarshal +// panics. +// +// MustMarshal is safe for concurrent use by multiple goroutines. +func MustMarshal(v interface{}) []interface{} { + r, err := Marshal(v) + if err != nil { + panic(err) + } + + return r +} + +// Unmarshal stores data from []interface{} in the struct value pointed to by +// v. +// +// Every considered struct field type must be one of the QL types or a type +// convertible to string, bool, int*, uint*, float* or complex* type or pointer +// to such type. Integers with a width dependent on the architecture can not be +// used. Only exported fields are considered. If an exported field QL tag +// contains "-" then such field is not considered. A QL tag is a struct tag +// part prefixed by "ql:". Fields are considered in the order of appearance. +// Types of values in data must be compatible with the corresponding considered +// field of v. +// +// If the struct has no ID field then the number of values in data must be equal +// to the number of considered fields of v. +// +// type T struct { +// A bool +// B string +// } +// +// Assuming the schema is +// +// CREATE TABLE T (A bool, B string); +// +// Data might be a result of queries like +// +// SELECT * FROM T; +// SELECT A, B FROM T; +// +// If the struct has a considered ID field then the number of values in data +// must be equal to the number of considered fields in v - or one less. In the +// later case the ID field is not set. +// +// type U struct { +// ID int64 +// A bool +// B string +// } +// +// Assuming the schema is +// +// CREATE TABLE T (A bool, B string); +// +// Data might be a result of queries like +// +// SELECT * FROM T; // ID not set +// SELECT A, B FROM T; // ID not set +// SELECT id(), A, B FROM T; // ID is set +// +// To unmarshal a value from data into a pointer field of v, Unmarshal first +// handles the case of the value being nil. In that case, Unmarshal sets the +// pointer to nil. Otherwise, Unmarshal unmarshals the data value into value +// pointed at by the pointer. If the pointer is nil, Unmarshal allocates a new +// value for it to point to. +// +// Unmarshal is safe for concurrent use by multiple goroutines. +func Unmarshal(v interface{}, data []interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + if err, ok = r.(error); !ok { + err = fmt.Errorf("%v", r) + } + err = fmt.Errorf("unmarshal: %v", err) + } + }() + + s, err := StructSchema(v) + if err != nil { + return err + } + + if !s.IsPtr { + return fmt.Errorf("unmarshal: need a pointer to a struct") + } + + id := false + nv, nf := len(data), len(s.Fields) + switch s.HasID { + case true: + switch { + case nv == nf: + id = true + case nv == nf-1: + // ok + default: + return fmt.Errorf("unmarshal: got %d values, need %d or %d", nv, nf-1, nf) + } + default: + switch { + case nv == nf: + // ok + default: + return fmt.Errorf("unmarshal: got %d values, need %d", nv, nf) + } + } + + j := 0 + vVal := reflect.ValueOf(v) + if s.IsPtr { + vVal = vVal.Elem() + } + for _, sf := range s.Fields { + if sf.IsID && !id { + continue + } + + d := data[j] + val := reflect.ValueOf(d) + j++ + + fVal := vVal.Field(sf.Index) + if u := sf.UnmarshalType; u != nil { + val = val.Convert(u) + } + if !sf.IsPtr { + fVal.Set(val) + continue + } + + if d == nil { + fVal.Set(sf.ZeroPtr) + continue + } + + if fVal.IsNil() { + fVal.Set(reflect.New(sf.ReflectType)) + } + + fVal.Elem().Set(val) + } + return nil +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/introspection_test.go b/Godeps/_workspace/src/github.com/cznic/ql/introspection_test.go new file mode 100644 index 000000000..a9a841942 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/introspection_test.go @@ -0,0 +1,1073 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "bytes" + "fmt" + "math/big" + //"reflect" + "testing" + "time" + + "github.com/cznic/mathutil" +) + +type ( + testSchema struct { + a int8 + ID int64 + A int8 + b int + B int `ql:"-"` + } + + testSchema2 struct{} + + testSchema3 struct { + a int8 + ID uint64 + A int8 + b int + B int `ql:"-"` + c bool + C bool `ql:"name cc"` + } + + testSchema4 struct { + a int8 + ID int64 `ql:"name id"` + A int8 + b int + B int `ql:"-"` + c bool + C bool `ql:"name cc"` + } + + testSchema5 struct { + I int `ql:"index x,uindex u"` + } + + testSchema6 struct { + A string `ql:"index x"` + } + + testSchema7 struct { + A int64 + B string `ql:"uindex x"` + C bool + } + + testSchema8 struct { + A bool + //B int + C int8 + D int16 + E int32 + F int64 + //G uint + H uint8 + I uint16 + J uint32 + K uint64 + L float32 + M float64 + N complex64 + O complex128 + P []byte + Q big.Int + R big.Rat + S string + T time.Time + U time.Duration + PA *bool + //PB *int + PC *int8 + PD *int16 + PE *int32 + PF *int64 + //PG *uint + PH *uint8 + PI *uint16 + PJ *uint32 + PK *uint64 + PL *float32 + PM *float64 + PN *complex64 + PO *complex128 + PP *[]byte + PQ *big.Int + PR *big.Rat + PS *string + PT *time.Time + PU *time.Duration + } + + testSchema9 struct { + i int + ID int64 `ql:"index xID"` + Other string `ql:"-"` + DepartmentName string `ql:"uindex xDepartmentName"` + } +) + +const ( + testSchemaSFFF = "begin transaction; create table if not exists testSchema (A int8); commit;" + testSchemaSFFT = "begin transaction; create table if not exists ql_testSchema (A int8); commit;" + testSchemaSFTF = "begin transaction; create table testSchema (A int8); commit;" + testSchemaSFTT = "begin transaction; create table ql_testSchema (A int8); commit;" + testSchemaSTFF = "create table if not exists testSchema (A int8)" + testSchemaSTFT = "create table if not exists ql_testSchema (A int8)" + testSchemaSTTF = "create table testSchema (A int8)" + testSchemaSTTT = "create table ql_testSchema (A int8)" + testSchema3S = "begin transaction; create table if not exists testSchema3 (ID uint64, A int8, cc bool); commit;" + testSchema4S = "begin transaction; create table if not exists testSchema4 (id int64, A int8, cc bool); commit;" + testSchema6S = "create table testSchema6 (A string); create index x on testSchema6 (A);" + testSchema7S = "begin transaction; create table testSchema7 (A int64, B string, C bool); create unique index x on testSchema7 (B); commit;" + testSchema8S = ` + begin transaction; + create table if not exists testSchema8 ( + A bool, + //B int64, + C int8, + D int16, + E int32, + F int64, + //G uint64, + H uint8, + I uint16, + J uint32, + K uint64, + L float32, + M float64, + N complex64, + O complex128, + P blob, + Q bigInt, + R bigRat, + S string, + T time, + U duration, + PA bool, + //PB int64, + PC int8, + PD int16, + PE int32, + PF int64, + //PG uint64, + PH uint8, + PI uint16, + PJ uint32, + PK uint64, + PL float32, + PM float64, + PN complex64, + PO complex128, + PP blob, + PQ bigInt, + PR bigRat, + PS string, + PT time, + PU duration, + ); + commit;` + testSchema9S = ` + begin transaction; + create table if not exists testSchema9 (DepartmentName string); + create index if not exists xID on testSchema9 (id()); + create unique index if not exists xDepartmentName on testSchema9 (DepartmentName); + commit;` +) + +func TestSchema(t *testing.T) { + tab := []struct { + inst interface{} + name string + opts *SchemaOptions + err bool + s string + }{ + // 0 + {inst: nil, err: true}, + {inst: interface{}(nil), err: true}, + {testSchema{}, "", nil, false, testSchemaSFFF}, + {testSchema{}, "", &SchemaOptions{}, false, testSchemaSFFF}, + {testSchema{}, "", &SchemaOptions{KeepPrefix: true}, false, testSchemaSFFT}, + // 5 + {testSchema{}, "", &SchemaOptions{NoIfNotExists: true}, false, testSchemaSFTF}, + {testSchema{}, "", &SchemaOptions{NoIfNotExists: true, KeepPrefix: true}, false, testSchemaSFTT}, + {testSchema{}, "", &SchemaOptions{NoTransaction: true}, false, testSchemaSTFF}, + {testSchema{}, "", &SchemaOptions{NoTransaction: true, KeepPrefix: true}, false, testSchemaSTFT}, + {testSchema{}, "", &SchemaOptions{NoTransaction: true, NoIfNotExists: true}, false, testSchemaSTTF}, + // 10 + {testSchema{}, "", &SchemaOptions{NoTransaction: true, NoIfNotExists: true, KeepPrefix: true}, false, testSchemaSTTT}, + {testSchema2{}, "", nil, true, ""}, + {testSchema3{}, "", nil, false, testSchema3S}, + {testSchema4{}, "", nil, false, testSchema4S}, + {testSchema5{}, "", nil, true, ""}, + // 15 + {testSchema6{}, "", &SchemaOptions{NoTransaction: true, NoIfNotExists: true}, false, testSchema6S}, + {testSchema7{}, "", &SchemaOptions{NoIfNotExists: true}, false, testSchema7S}, + {testSchema8{}, "", nil, false, testSchema8S}, + {&testSchema8{}, "", nil, false, testSchema8S}, + {&testSchema9{}, "", nil, false, testSchema9S}, + } + + for iTest, test := range tab { + l, err := Schema(test.inst, test.name, test.opts) + if g, e := err != nil, test.err; g != e { + t.Fatal(iTest, g, e, err) + } + + if err != nil { + t.Log(iTest, err) + continue + } + + s, err := Compile(test.s) + if err != nil { + panic("internal error 055") + } + + if g, e := l.String(), s.String(); g != e { + t.Fatalf("%d\n----\n%s\n----\n%s", iTest, g, e) + } + } +} + +func ExampleSchema() { + type department struct { + a int // unexported -> ignored + ID int64 `ql:"index xID"` + Other string `xml:"-" ql:"-"` // ignored by QL tag + DepartmentName string `ql:"name Name, uindex xName" json:"foo"` + m bool + HQ int32 + z string + } + + schema := MustSchema((*department)(nil), "", nil) + sel := MustCompile(` + SELECT * FROM __Table WHERE !hasPrefix(Name, "__") ORDER BY Name; + SELECT * FROM __Column WHERE !hasPrefix(TableName, "__") ORDER BY TableName, Ordinal; + SELECT * FROM __Index WHERE !hasPrefix(TableName, "__") ORDER BY Name, ColumnName;`, + ) + fmt.Print(schema) + + db, err := OpenMem() + if err != nil { + panic(err) + } + + if _, _, err = db.Execute(NewRWCtx(), schema); err != nil { + panic(err) + } + + rs, _, err := db.Execute(nil, sel) + if err != nil { + panic(err) + } + + for _, rs := range rs { + fmt.Println("----") + if err = rs.Do(true, func(data []interface{}) (bool, error) { + fmt.Println(data) + return true, nil + }); err != nil { + panic(err) + } + } + // Output: + // BEGIN TRANSACTION; + // CREATE TABLE IF NOT EXISTS department (Name string, HQ int32); + // CREATE INDEX IF NOT EXISTS xID ON department (id()); + // CREATE UNIQUE INDEX IF NOT EXISTS xName ON department (Name); + // COMMIT; + // ---- + // [Name Schema] + // [department CREATE TABLE department (Name string, HQ int32);] + // ---- + // [TableName Ordinal Name Type] + // [department 1 Name string] + // [department 2 HQ int32] + // ---- + // [TableName ColumnName Name IsUnique] + // [department id() xID false] + // [department Name xName true] +} + +func TestMarshal(t *testing.T) { + now := time.Now() + dur := time.Millisecond + schema8 := testSchema8{ + A: true, + //B: 1, + C: 2, + D: 3, + E: 4, + F: 5, + //G: 6, + H: 7, + I: 8, + J: 9, + K: 10, + L: 11, + M: 12, + N: -1, + O: -2, + P: []byte("abc"), + Q: *big.NewInt(1), + R: *big.NewRat(3, 2), + S: "string", + T: now, + U: dur, + } + schema8.PA = &schema8.A + //schema8.PB = &schema8.B + schema8.PC = &schema8.C + schema8.PD = &schema8.D + schema8.PE = &schema8.E + schema8.PF = &schema8.F + //schema8.PG = &schema8.G + schema8.PH = &schema8.H + schema8.PI = &schema8.I + schema8.PJ = &schema8.J + schema8.PK = &schema8.K + schema8.PL = &schema8.L + schema8.PM = &schema8.M + schema8.PN = &schema8.N + schema8.PO = &schema8.O + schema8.PP = &schema8.P + schema8.PQ = &schema8.Q + schema8.PR = &schema8.R + schema8.PS = &schema8.S + schema8.PT = &schema8.T + schema8.PU = &schema8.U + + type u int + tab := []struct { + inst interface{} + err bool + r []interface{} + }{ + {42, true, nil}, + {new(u), true, nil}, + {testSchema8{}, false, []interface{}{ + false, + //int64(0), + int8(0), + int16(0), + int32(0), + int64(0), + //uint64(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + float32(0), + float64(0), + complex64(0), + complex128(0), + []byte(nil), + big.Int{}, + big.Rat{}, + "", + time.Time{}, + time.Duration(0), + nil, + //nil, + nil, + nil, + nil, + nil, + //nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }}, + {&testSchema8{}, false, []interface{}{ + false, + //int64(0), + int8(0), + int16(0), + int32(0), + int64(0), + //uint64(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + float32(0), + float64(0), + complex64(0), + complex128(0), + []byte(nil), + big.Int{}, + big.Rat{}, + "", + time.Time{}, + time.Duration(0), + nil, + //nil, + nil, + nil, + nil, + nil, + //nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }}, + {schema8, false, []interface{}{ + true, + //int64(1), + int8(2), + int16(3), + int32(4), + int64(5), + //uint64(6), + uint8(7), + uint16(8), + uint32(9), + uint64(10), + float32(11), + float64(12), + complex64(-1), + complex128(-2), + []byte("abc"), + *big.NewInt(1), + *big.NewRat(3, 2), + "string", + now, + dur, + true, + //int64(1), + int8(2), + int16(3), + int32(4), + int64(5), + //uint64(6), + uint8(7), + uint16(8), + uint32(9), + uint64(10), + float32(11), + float64(12), + complex64(-1), + complex128(-2), + []byte("abc"), + *big.NewInt(1), + *big.NewRat(3, 2), + "string", + now, + dur, + }}, + {&schema8, false, []interface{}{ + true, + //int64(1), + int8(2), + int16(3), + int32(4), + int64(5), + //uint64(6), + uint8(7), + uint16(8), + uint32(9), + uint64(10), + float32(11), + float64(12), + complex64(-1), + complex128(-2), + []byte("abc"), + *big.NewInt(1), + *big.NewRat(3, 2), + "string", + now, + dur, + true, + //int64(1), + int8(2), + int16(3), + int32(4), + int64(5), + //uint64(6), + uint8(7), + uint16(8), + uint32(9), + uint64(10), + float32(11), + float64(12), + complex64(-1), + complex128(-2), + []byte("abc"), + *big.NewInt(1), + *big.NewRat(3, 2), + "string", + now, + dur, + }}, + } + for iTest, test := range tab { + r, err := Marshal(test.inst) + if g, e := err != nil, test.err; g != e { + t.Fatal(iTest, g, e) + } + + if err != nil { + t.Log(err) + continue + } + + for i := 0; i < mathutil.Min(len(r), len(test.r)); i++ { + g, e := r[i], test.r[i] + use(e) + switch x := g.(type) { + case bool: + switch y := e.(type) { + case bool: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case int: + switch y := e.(type) { + case int64: + if int64(x) != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case int8: + switch y := e.(type) { + case int8: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case int16: + switch y := e.(type) { + case int16: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case int32: + switch y := e.(type) { + case int32: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case int64: + switch y := e.(type) { + case int64: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case uint: + switch y := e.(type) { + case uint64: + if uint64(x) != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case uint8: + switch y := e.(type) { + case uint8: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case uint16: + switch y := e.(type) { + case uint16: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case uint32: + switch y := e.(type) { + case uint32: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case uint64: + switch y := e.(type) { + case uint64: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case float32: + switch y := e.(type) { + case float32: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case float64: + switch y := e.(type) { + case float64: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case complex64: + switch y := e.(type) { + case complex64: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case complex128: + switch y := e.(type) { + case complex128: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case []byte: + switch y := e.(type) { + case []byte: + if bytes.Compare(x, y) != 0 { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case big.Int: + switch y := e.(type) { + case big.Int: + if x.Cmp(&y) != 0 { + t.Fatal(iTest, &x, &y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case big.Rat: + switch y := e.(type) { + case big.Rat: + if x.Cmp(&y) != 0 { + t.Fatal(iTest, &x, &y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case string: + switch y := e.(type) { + case string: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case time.Time: + switch y := e.(type) { + case time.Time: + if !x.Equal(y) { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case time.Duration: + switch y := e.(type) { + case time.Duration: + if x != y { + t.Fatal(iTest, x, y) + } + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + case nil: + switch y := e.(type) { + case nil: + // ok + default: + t.Fatalf("%d: %T <-> %T", iTest, x, y) + } + default: + panic(fmt.Errorf("%T", x)) + } + } + + if g, e := len(r), len(test.r); g != e { + t.Fatal(iTest, g, e) + } + + } +} + +func ExampleMarshal() { + type myInt int16 + + type myString string + + type item struct { + ID int64 + Name myString + Qty *myInt // pointer enables nil values + Bar int8 + } + + schema := MustSchema((*item)(nil), "", nil) + ins := MustCompile(` + BEGIN TRANSACTION; + INSERT INTO item VALUES($1, $2, $3); + COMMIT;`, + ) + + db, err := OpenMem() + if err != nil { + panic(err) + } + + ctx := NewRWCtx() + if _, _, err := db.Execute(ctx, schema); err != nil { + panic(err) + } + + if _, _, err := db.Execute(ctx, ins, MustMarshal(&item{Name: "foo", Bar: -1})...); err != nil { + panic(err) + } + + q := myInt(42) + if _, _, err := db.Execute(ctx, ins, MustMarshal(&item{Name: "bar", Qty: &q})...); err != nil { + panic(err) + } + + rs, _, err := db.Run(nil, "SELECT * FROM item ORDER BY id();") + if err != nil { + panic(err) + } + + if err = rs[0].Do(true, func(data []interface{}) (bool, error) { + fmt.Println(data) + return true, nil + }); err != nil { + panic(err) + } + // Output: + // [Name Qty Bar] + // [foo -1] + // [bar 42 0] +} + +func TestUnmarshal0(t *testing.T) { + type t1 struct { + I, J int64 + } + + // ---- value field + v1 := &t1{-1, -2} + if err := Unmarshal(v1, []interface{}{int64(42), int64(314)}); err != nil { + t.Fatal(err) + } + + if g, e := v1.I, int64(42); g != e { + t.Fatal(g, e) + } + + if g, e := v1.J, int64(314); g != e { + t.Fatal(g, e) + } + + type t2 struct { + P *int64 + } + + // ---- nil into nil ptr field + v2 := &t2{P: nil} + if err := Unmarshal(v2, []interface{}{nil}); err != nil { + t.Fatal(err) + } + + if g, e := v2.P, (*int64)(nil); g != e { + t.Fatal(g, e) + } + + v2 = &t2{P: nil} + if err := Unmarshal(v2, []interface{}{interface{}(nil)}); err != nil { + t.Fatal(err) + } + + if g, e := v2.P, (*int64)(nil); g != e { + t.Fatal(g, e) + } + + // ---- nil into non nil ptr field + i := int64(42) + v2 = &t2{P: &i} + if err := Unmarshal(v2, []interface{}{nil}); err != nil { + t.Fatal(err) + } + + if g, e := v2.P, (*int64)(nil); g != e { + t.Fatal(g, e) + } + + if g, e := i, int64(42); g != e { + t.Fatal(g, e) + } + + v2 = &t2{P: &i} + if err := Unmarshal(v2, []interface{}{interface{}(nil)}); err != nil { + t.Fatal(err) + } + + if g, e := v2.P, (*int64)(nil); g != e { + t.Fatal(g, e) + } + + if g, e := i, int64(42); g != e { + t.Fatal(g, e) + } + + // ---- non nil value into non nil ptr field + i = 42 + v2 = &t2{P: &i} + if err := Unmarshal(v2, []interface{}{int64(314)}); err != nil { + t.Fatal(err) + } + + if g, e := v2.P, &i; g != e { + t.Fatal(g, e) + } + + if g, e := i, int64(314); g != e { + t.Fatal(g, e) + } + + // ---- non nil value into nil ptr field + v2 = &t2{P: nil} + if err := Unmarshal(v2, []interface{}{int64(314)}); err != nil { + t.Fatal(err) + } + + if g, e := v2.P != nil, true; g != e { + t.Fatal(g, e) + } + + if g, e := *v2.P, int64(314); g != e { + t.Fatal(g, e) + } +} + +func TestUnmarshal(t *testing.T) { + type myString string + + type t1 struct { + A bool + B myString + } + + type t2 struct { + A bool + ID int64 + B myString + } + + f := func(v interface{}) int64 { + if x, ok := v.(*t2); ok { + return x.ID + } + + return -1 + } + + tab := []struct { + inst interface{} + data []interface{} + err bool + }{ + // 0 + {t1{}, []interface{}{true, "foo"}, true}, // not a ptr + {&t1{}, []interface{}{true}, true}, // too few values + {&t1{}, []interface{}{"foo"}, true}, // too few values + {&t1{}, []interface{}{true, "foo", 42}, true}, // too many values + {&t1{}, []interface{}{"foo", true, 42}, true}, // too many values + // 5 + {&t1{}, []interface{}{true, "foo"}, false}, + {&t1{}, []interface{}{false, "bar"}, false}, + {&t1{}, []interface{}{"bar", "baz"}, true}, + {&t1{}, []interface{}{true, 42.7}, true}, + {&t2{}, []interface{}{1}, true}, // too few values + // 10 + {&t2{}, []interface{}{1, 2, 3, 4}, true}, // too many values + {&t2{}, []interface{}{false, int64(314), "foo"}, false}, + {&t2{}, []interface{}{true, int64(42), "foo"}, false}, + {&t2{}, []interface{}{false, "foo"}, false}, + // 15 + {&t2{}, []interface{}{true, "foo"}, false}, + } + + for iTest, test := range tab { + inst := test.inst + err := Unmarshal(inst, test.data) + if g, e := err != nil, test.err; g != e { + t.Fatal(iTest, g, e) + } + + if err != nil { + t.Log(iTest, err) + continue + } + + data, err := Marshal(inst) + if err != nil { + t.Fatal(iTest, err) + } + + if g, e := len(data), len(test.data); g > e { + t.Fatal(iTest, g, e) + } + + j := 0 + for _, v := range data { + v2 := test.data[j] + j++ + if _, ok := v2.(int64); ok { + if g, e := f(inst), v2; g != e { + t.Fatal(iTest, g, e) + } + + continue + } + + if g, e := v, v2; g != e { + t.Fatal(iTest, g, e) + } + } + } +} + +func ExampleUnmarshal() { + type myString string + + type row struct { + ID int64 + S myString + P *int64 + } + + schema := MustSchema((*row)(nil), "", nil) + ins := MustCompile(` + BEGIN TRANSACTION; + INSERT INTO row VALUES($1, $2); + COMMIT;`, + ) + sel := MustCompile(` + SELECT id(), S, P FROM row ORDER by id(); + SELECT * FROM row ORDER by id();`, + ) + + db, err := OpenMem() + if err != nil { + panic(err) + } + + ctx := NewRWCtx() + if _, _, err = db.Execute(ctx, schema); err != nil { + panic(err) + } + + r := &row{S: "foo"} + if _, _, err = db.Execute(ctx, ins, MustMarshal(r)...); err != nil { + panic(err) + } + + i42 := int64(42) + r = &row{S: "bar", P: &i42} + if _, _, err = db.Execute(ctx, ins, MustMarshal(r)...); err != nil { + panic(err) + } + + rs, _, err := db.Execute(nil, sel) + if err != nil { + panic(err) + } + + for _, rs := range rs { + fmt.Println("----") + if err := rs.Do(false, func(data []interface{}) (bool, error) { + r := &row{} + if err := Unmarshal(r, data); err != nil { + return false, err + } + + fmt.Printf("ID %d, S %q, P ", r.ID, r.S) + switch r.P == nil { + case true: + fmt.Println("") + default: + fmt.Println(*r.P) + } + return true, nil + }); err != nil { + panic(err) + } + } + // Output: + // ---- + // ID 1, S "foo", P + // ID 2, S "bar", P 42 + // ---- + // ID 0, S "foo", P + // ID 0, S "bar", P 42 +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/mem.go b/Godeps/_workspace/src/github.com/cznic/ql/mem.go new file mode 100644 index 000000000..bd2a2dd26 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/mem.go @@ -0,0 +1,1277 @@ +// Copyright (c) 2014 ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Plain memory storage back end. + +package ql + +import ( + "bytes" + "fmt" + "io" + "math/big" + "time" +) + +var ( + _ btreeIndex = (*memIndex)(nil) + _ btreeIterator = (*memBTreeIterator)(nil) + _ indexIterator = (*xenumerator2)(nil) + _ storage = (*mem)(nil) + _ temp = (*memTemp)(nil) +) + +type memIndex struct { + m *mem + t *xtree + unique bool +} + +func newMemIndex(m *mem, unique bool) *memIndex { + r := &memIndex{t: xtreeNew(), unique: unique, m: m} + //dbg("newMemIndex %p, %p", r, m) + return r +} + +func (x *memIndex) Clear() error { + //dbg("memIndex(%p, %p).Clear", x, x.m) + x.m.newUndo(undoClearX, 0, []interface{}{x, x.t}) + x.t = xtreeNew() + return nil +} + +func (x *memIndex) Create(indexedValues []interface{}, h int64) error { + //dbg("memIndex(%p, %p).Create %v, %v", x, x.m, indexedValues, h) + t := x.t + switch { + case !x.unique: + k := indexKey{indexedValues, h} + x.m.newUndo(undoCreateX, 0, []interface{}{x, k}) //TODO why is old value, if any, not saved? + t.Set(k, 0) + case isIndexNull(indexedValues): // unique, NULL + k := indexKey{nil, h} + x.m.newUndo(undoCreateX, 0, []interface{}{x, k}) //TODO why is old value, if any, not saved? + t.Set(k, 0) + default: // unique, non NULL + k := indexKey{indexedValues, 0} + if _, ok := t.Get(k); ok { //LATER need .Put + return fmt.Errorf("cannot insert into unique index: duplicate value(s): %v", indexedValues) + } + + x.m.newUndo(undoCreateX, 0, []interface{}{x, k}) //TODO why is old value, if any, not saved? + t.Set(k, int(h)) + } + return nil +} + +func (x *memIndex) Delete(indexedValues []interface{}, h int64) error { + //dbg("memIndex(%p, %p).Delete %v, %v", x, x.m, indexedValues, h) + t := x.t + var k indexKey + var v interface{} + var ok, okv bool + switch { + case !x.unique: + k = indexKey{indexedValues, h} + v, okv = t.Get(k) + ok = t.delete(k) + case isIndexNull(indexedValues): // unique, NULL + k = indexKey{nil, h} + v, okv = t.Get(k) + ok = t.delete(k) + default: // unique, non NULL + k = indexKey{indexedValues, 0} + v, okv = t.Get(k) + ok = t.delete(k) + } + if ok { + if okv { + x.m.newUndo(undoDeleteX, int64(v.(int)), []interface{}{x, k}) + } + return nil + } + + return fmt.Errorf("internal error 047") +} + +func (x *memIndex) Drop() error { + x.m.newUndo(undoDropX, 0, []interface{}{x, *x}) + *x = memIndex{} + return nil +} + +func (x *memIndex) Seek(indexedValues []interface{}) (indexIterator, bool, error) { + it, hit := x.t.Seek(indexKey{indexedValues, 0}) + return &xenumerator2{*it, x.unique}, hit, nil +} + +func (x *memIndex) SeekFirst() (iter indexIterator, err error) { + it, err := x.t.SeekFirst() + if err != nil { + return nil, err + } + + return &xenumerator2{*it, x.unique}, nil +} + +func (x *memIndex) SeekLast() (iter indexIterator, err error) { + it, err := x.t.SeekLast() + if err != nil { + return nil, err + } + + return &xenumerator2{*it, x.unique}, nil +} + +type xenumerator2 struct { + it xenumerator + unique bool +} + +func (it *xenumerator2) Next() ([]interface{}, int64, error) { + k, h, err := it.it.Next() + if err != nil { + return nil, -1, err + } + + switch it.unique { + case true: + if k.value == nil { + return nil, k.h, nil + } + + return k.value, h, nil + default: + return k.value, k.h, nil + } +} + +func (it *xenumerator2) Prev() ([]interface{}, int64, error) { + k, h, err := it.it.Prev() + if err != nil { + return nil, -1, err + } + + switch it.unique { + case true: + if k.value == nil { + return nil, k.h, nil + } + + return k.value, h, nil + default: + return k.value, k.h, nil + } +} + +type memBTreeIterator enumerator + +func (it *memBTreeIterator) Next() (k, v []interface{}, err error) { + return (*enumerator)(it).Next() +} + +type memTemp struct { + tree *tree + store *mem +} + +func (t *memTemp) BeginTransaction() (err error) { + return nil +} + +func (t *memTemp) Get(k []interface{}) (v []interface{}, err error) { + v, _ = t.tree.Get(k) + return +} + +func (t *memTemp) Create(data ...interface{}) (h int64, err error) { + s := t.store + switch n := len(s.recycler); { + case n != 0: + h = int64(s.recycler[n-1]) + s.recycler = s.recycler[:n-1] + s.data[h] = s.clone(data...) + default: + h = int64(len(s.data)) + s.data = append(s.data, s.clone(data...)) + } + return +} + +func (t *memTemp) Read(dst []interface{}, h int64, cols ...*col) (data []interface{}, err error) { + return t.store.Read(dst, h, cols...) +} + +func (*memTemp) Drop() (err error) { return } + +func (t *memTemp) Set(k, v []interface{}) (err error) { + t.tree.Set(append([]interface{}(nil), k...), t.store.clone(v...)) + return +} + +func (t *memTemp) SeekFirst() (e btreeIterator, err error) { + en, err := t.tree.SeekFirst() + if err != nil { + return + } + + return (*memBTreeIterator)(en), nil +} + +const ( + undoCreateNewHandle = iota + undoCreateRecycledHandle + undoUpdate + undoDelete + undoClearX // {0: *memIndex, 1: *xtree} + undoCreateX // {0: *memIndex, 1: indexKey} + undoDeleteX // {0: *memIndex, 1: indexKey} + undoDropX // {0: *memIndex, 1: memIndex} +) + +type undo struct { + tag int + h int64 + data []interface{} +} + +type undos struct { + list []undo + parent *undos +} + +type mem struct { + data [][]interface{} + id int64 + recycler []int + tnl int + rollback *undos +} + +func newMemStorage() (s *mem, err error) { + s = &mem{data: [][]interface{}{nil}} + if err = s.BeginTransaction(); err != nil { + return nil, err + } + + h, err := s.Create() + if h != 1 { + panic("internal error 048") + } + + if err = s.Commit(); err != nil { + return nil, err + } + + return +} + +func (s *mem) OpenIndex(unique bool, handle int64) (btreeIndex, error) { // Never called on the memory backend. + panic("internal error 049") +} + +func (s *mem) newUndo(tag int, h int64, data []interface{}) { + s.rollback.list = append(s.rollback.list, undo{tag, h, data}) +} + +func (s *mem) Acid() bool { return false } + +func (s *mem) Close() (err error) { + if s.tnl != 0 { + return fmt.Errorf("cannot close DB while open transaction exist") + } + *s = mem{} + return +} + +func (s *mem) CreateIndex(unique bool) ( /* handle */ int64, btreeIndex, error) { + return -1, newMemIndex(s, unique), nil // handle of memIndex should never be used +} + +func (s *mem) Name() string { return fmt.Sprintf("/proc/self/mem/%p", s) } // fake, non existing name + +// OpenMem returns a new, empty DB backed by the process' memory. The back end +// has no limits on field/record/table/DB size other than memory available to +// the process. +func OpenMem() (db *DB, err error) { + s, err := newMemStorage() + if err != nil { + return + } + + if db, err = newDB(s); err != nil { + return nil, err + } + + db.isMem = true + return db, nil +} + +func (s *mem) Verify() (allocs int64, err error) { + for _, v := range s.recycler { + if s.data[v] != nil { + return 0, fmt.Errorf("corrupted: non nil free handle %d", s.data[v]) + } + } + + for _, v := range s.data { + if v != nil { + allocs++ + } + } + + if allocs != int64(len(s.data))-1-int64(len(s.recycler)) { + return 0, fmt.Errorf("corrupted: len(data) %d, len(recycler) %d, allocs %d", len(s.data), len(s.recycler), allocs) + } + + return +} + +func (s *mem) String() string { + var b bytes.Buffer + for i, v := range s.data { + b.WriteString(fmt.Sprintf("s.data[%d] %#v\n", i, v)) + } + for i, v := range s.recycler { + b.WriteString(fmt.Sprintf("s.recycler[%d] %v\n", i, v)) + } + return b.String() +} + +func (s *mem) CreateTemp(asc bool) (_ temp, err error) { + st, err := newMemStorage() + if err != nil { + return + } + + return &memTemp{ + tree: treeNew(collators[asc]), + store: st, + }, nil +} + +func (s *mem) ResetID() (err error) { + s.id = 0 + return +} + +func (s *mem) ID() (id int64, err error) { + s.id++ + return s.id, nil +} + +func (s *mem) clone(data ...interface{}) []interface{} { + r := make([]interface{}, len(data)) + for i, v := range data { + switch x := v.(type) { + case nil: + // nop + case idealComplex: + r[i] = complex128(x) + case idealFloat: + r[i] = float64(x) + case idealInt: + r[i] = int64(x) + case idealRune: + r[i] = int32(x) + case idealUint: + r[i] = uint64(x) + case bool: + r[i] = x + case complex64: + r[i] = x + case complex128: + r[i] = x + case float32: + r[i] = x + case float64: + r[i] = x + case int: + r[i] = int64(x) + case int8: + r[i] = x + case int16: + r[i] = x + case int32: + r[i] = x + case int64: + r[i] = x + case string: + r[i] = x + case uint: + r[i] = uint64(x) + case uint8: + r[i] = x + case uint16: + r[i] = x + case uint32: + r[i] = x + case uint64: + r[i] = x + case []byte: + r[i] = append([]byte(nil), x...) + case *big.Int: + r[i] = big.NewInt(0).Set(x) + case *big.Rat: + r[i] = big.NewRat(1, 2).Set(x) + case time.Time: + t := x + r[i] = t + case time.Duration: + r[i] = x + case map[string]interface{}: // map of ids of a cross join + r[i] = x + default: + panic("internal error 050") + } + } + return r +} + +func (s *mem) Create(data ...interface{}) (h int64, err error) { + switch n := len(s.recycler); { + case n != 0: + h = int64(s.recycler[n-1]) + s.recycler = s.recycler[:n-1] + s.data[h] = s.clone(data...) + r := s.rollback + r.list = append(r.list, undo{ + tag: undoCreateRecycledHandle, + h: h, + }) + default: + h = int64(len(s.data)) + s.data = append(s.data, s.clone(data...)) + r := s.rollback + r.list = append(r.list, undo{ + tag: undoCreateNewHandle, + h: h, + }) + } + return +} + +func (s *mem) Read(dst []interface{}, h int64, cols ...*col) (data []interface{}, err error) { + if i := int(h); i != 0 && i < len(s.data) { + d := s.clone(s.data[h]...) + if cols == nil { + return d, nil + } + + for n, dn := len(cols)+2, len(d); dn < n; dn++ { + d = append(d, nil) + } + return d, nil + } + + return nil, errNoDataForHandle +} + +func (s *mem) UpdateRow(h int64, _ []*col, data ...interface{}) (err error) { + return s.Update(h, data...) +} + +func (s *mem) Update(h int64, data ...interface{}) (err error) { + r := s.rollback + r.list = append(r.list, undo{ + tag: undoUpdate, + h: h, + data: s.data[h], + }) + s.data[h] = s.clone(data...) + return +} + +func (s *mem) Delete(h int64, _ ...*col) (err error) { + r := s.rollback + r.list = append(r.list, undo{ + tag: undoDelete, + h: h, + data: s.data[h], + }) + s.recycler = append(s.recycler, int(h)) + s.data[h] = nil + return +} + +func (s *mem) BeginTransaction() (err error) { + s.rollback = &undos{parent: s.rollback} + s.tnl++ + return nil +} + +func (s *mem) Rollback() (err error) { + if s.tnl == 0 { + return errRollbackNotInTransaction + } + + list := s.rollback.list + for i := len(list) - 1; i >= 0; i-- { + undo := list[i] + switch h, data := int(undo.h), undo.data; undo.tag { + case undoCreateNewHandle: + d := s.data + s.data = d[:len(d)-1] + case undoCreateRecycledHandle: + s.data[h] = nil + r := s.recycler + s.recycler = append(r, h) + case undoUpdate: + s.data[h] = data + case undoDelete: + s.data[h] = data + s.recycler = s.recycler[:len(s.recycler)-1] + case undoClearX: + x, t := data[0].(*memIndex), data[1].(*xtree) + x.t = t + case undoCreateX: + x, k := data[0].(*memIndex), data[1].(indexKey) + x.t.delete(k) + case undoDeleteX: + x, k := data[0].(*memIndex), data[1].(indexKey) + x.t.Set(k, h) + case undoDropX: + x, v := data[0].(*memIndex), data[1].(memIndex) + *x = v + default: + panic("internal error 051") + } + } + + s.tnl-- + s.rollback = s.rollback.parent + return nil +} + +func (s *mem) Commit() (err error) { + if s.tnl == 0 { + return errCommitNotInTransaction + } + + s.tnl-- + s.rollback = s.rollback.parent + return nil +} + +// Transaction index B+Tree +//LATER make it just a wrapper of the implementation in btree.go. + +type ( + xd struct { // data page + c int + xd [2*kd + 1]xde + n *xd + p *xd + } + + xde struct { // xd element + k indexKey + v int + } + + // xenumerator captures the state of enumerating a tree. It is returned + // from the Seek* methods. The enumerator is aware of any mutations + // made to the tree in the process of enumerating it and automatically + // resumes the enumeration at the proper key, if possible. + // + // However, once an xenumerator returns io.EOF to signal "no more + // items", it does no more attempt to "resync" on tree mutation(s). In + // other words, io.EOF from an Enumaretor is "sticky" (idempotent). + xenumerator struct { + err error + hit bool + i int + k indexKey + q *xd + t *xtree + ver int64 + } + + // xtree is a B+tree. + xtree struct { + c int + first *xd + last *xd + r interface{} + ver int64 + } + + xxe struct { // xx element + ch interface{} + sep *xd + } + + xx struct { // index page + c int + xx [2*kx + 2]xxe + } +) + +func (a *indexKey) cmp(b *indexKey) int { + r := collate(a.value, b.value) + if r != 0 { + return r + } + + return int(a.h) - int(b.h) +} + +var ( // R/O zero values + zxd xd + zxde xde + zxx xx + zxxe xxe +) + +func xclr(q interface{}) { + switch xx := q.(type) { + case *xx: + for i := 0; i <= xx.c; i++ { // Ch0 Sep0 ... Chn-1 Sepn-1 Chn + xclr(xx.xx[i].ch) + } + *xx = zxx // GC + case *xd: + *xx = zxd // GC + } +} + +// -------------------------------------------------------------------------- xx + +func xnewX(ch0 interface{}) *xx { + r := &xx{} + r.xx[0].ch = ch0 + return r +} + +func (q *xx) extract(i int) { + q.c-- + if i < q.c { + copy(q.xx[i:], q.xx[i+1:q.c+1]) + q.xx[q.c].ch = q.xx[q.c+1].ch + q.xx[q.c].sep = nil // GC + q.xx[q.c+1] = zxxe // GC + } +} + +func (q *xx) insert(i int, xd *xd, ch interface{}) *xx { + c := q.c + if i < c { + q.xx[c+1].ch = q.xx[c].ch + copy(q.xx[i+2:], q.xx[i+1:c]) + q.xx[i+1].sep = q.xx[i].sep + } + c++ + q.c = c + q.xx[i].sep = xd + q.xx[i+1].ch = ch + return q +} + +func (q *xx) siblings(i int) (l, r *xd) { + if i >= 0 { + if i > 0 { + l = q.xx[i-1].ch.(*xd) + } + if i < q.c { + r = q.xx[i+1].ch.(*xd) + } + } + return +} + +// -------------------------------------------------------------------------- xd + +func (l *xd) mvL(r *xd, c int) { + copy(l.xd[l.c:], r.xd[:c]) + copy(r.xd[:], r.xd[c:r.c]) + l.c += c + r.c -= c +} + +func (l *xd) mvR(r *xd, c int) { + copy(r.xd[c:], r.xd[:r.c]) + copy(r.xd[:c], l.xd[l.c-c:]) + r.c += c + l.c -= c +} + +// ----------------------------------------------------------------------- xtree + +// xtreeNew returns a newly created, empty xtree. The compare function is used +// for key collation. +func xtreeNew() *xtree { + return &xtree{} +} + +// Clear removes all K/V pairs from the tree. +func (t *xtree) Clear() { + if t.r == nil { + return + } + + xclr(t.r) + t.c, t.first, t.last, t.r = 0, nil, nil, nil + t.ver++ +} + +func (t *xtree) cat(p *xx, q, r *xd, pi int) { + t.ver++ + q.mvL(r, r.c) + if r.n != nil { + r.n.p = q + } else { + t.last = q + } + q.n = r.n + if p.c > 1 { + p.extract(pi) + p.xx[pi].ch = q + } else { + t.r = q + } +} + +func (t *xtree) catX(p, q, r *xx, pi int) { + t.ver++ + q.xx[q.c].sep = p.xx[pi].sep + copy(q.xx[q.c+1:], r.xx[:r.c]) + q.c += r.c + 1 + q.xx[q.c].ch = r.xx[r.c].ch + if p.c > 1 { + p.c-- + pc := p.c + if pi < pc { + p.xx[pi].sep = p.xx[pi+1].sep + copy(p.xx[pi+1:], p.xx[pi+2:pc+1]) + p.xx[pc].ch = p.xx[pc+1].ch + p.xx[pc].sep = nil // GC + p.xx[pc+1].ch = nil // GC + } + return + } + + t.r = q +} + +//Delete removes the k's KV pair, if it exists, in which case Delete returns +//true. +func (t *xtree) delete(k indexKey) (ok bool) { + pi := -1 + var p *xx + q := t.r + if q == nil { + return + } + + for { + var i int + i, ok = t.find(q, k) + if ok { + switch xx := q.(type) { + case *xx: + dp := xx.xx[i].sep + switch { + case dp.c > kd: + t.extract(dp, 0) + default: + if xx.c < kx && q != t.r { + t.underflowX(p, &xx, pi, &i) + } + pi = i + 1 + p = xx + q = xx.xx[pi].ch + ok = false + continue + } + case *xd: + t.extract(xx, i) + if xx.c >= kd { + return + } + + if q != t.r { + t.underflow(p, xx, pi) + } else if t.c == 0 { + t.Clear() + } + } + return + } + + switch xx := q.(type) { + case *xx: + if xx.c < kx && q != t.r { + t.underflowX(p, &xx, pi, &i) + } + pi = i + p = xx + q = xx.xx[i].ch + case *xd: + return + } + } +} + +func (t *xtree) extract(q *xd, i int) { // (r int64) { + t.ver++ + //r = q.xd[i].v // prepared for Extract + q.c-- + if i < q.c { + copy(q.xd[i:], q.xd[i+1:q.c+1]) + } + q.xd[q.c] = zxde // GC + t.c-- + return +} + +func (t *xtree) find(q interface{}, k indexKey) (i int, ok bool) { + var mk indexKey + l := 0 + switch xx := q.(type) { + case *xx: + h := xx.c - 1 + for l <= h { + m := (l + h) >> 1 + mk = xx.xx[m].sep.xd[0].k + switch cmp := k.cmp(&mk); { + case cmp > 0: + l = m + 1 + case cmp == 0: + return m, true + default: + h = m - 1 + } + } + case *xd: + h := xx.c - 1 + for l <= h { + m := (l + h) >> 1 + mk = xx.xd[m].k + switch cmp := k.cmp(&mk); { + case cmp > 0: + l = m + 1 + case cmp == 0: + return m, true + default: + h = m - 1 + } + } + } + return l, false +} + +// First returns the first item of the tree in the key collating order, or +// (nil, nil) if the tree is empty. +func (t *xtree) First() (k indexKey, v int) { + if q := t.first; q != nil { + q := &q.xd[0] + k, v = q.k, q.v + } + return +} + +// Get returns the value associated with k and true if it exists. Otherwise Get +// returns (nil, false). +func (t *xtree) Get(k indexKey) (v int, ok bool) { + q := t.r + if q == nil { + return + } + + for { + var i int + if i, ok = t.find(q, k); ok { + switch xx := q.(type) { + case *xx: + return xx.xx[i].sep.xd[0].v, true + case *xd: + return xx.xd[i].v, true + } + } + switch xx := q.(type) { + case *xx: + q = xx.xx[i].ch + default: + return + } + } +} + +func (t *xtree) insert(q *xd, i int, k indexKey, v int) *xd { + t.ver++ + c := q.c + if i < c { + copy(q.xd[i+1:], q.xd[i:c]) + } + c++ + q.c = c + q.xd[i].k, q.xd[i].v = k, v + t.c++ + return q +} + +// Last returns the last item of the tree in the key collating order, or (nil, +// nil) if the tree is empty. +func (t *xtree) Last() (k indexKey, v int) { + if q := t.last; q != nil { + q := &q.xd[q.c-1] + k, v = q.k, q.v + } + return +} + +// Len returns the number of items in the tree. +func (t *xtree) Len() int { + return t.c +} + +func (t *xtree) overflow(p *xx, q *xd, pi, i int, k indexKey, v int) { + t.ver++ + l, r := p.siblings(pi) + + if l != nil && l.c < 2*kd { + l.mvL(q, 1) + t.insert(q, i-1, k, v) + return + } + + if r != nil && r.c < 2*kd { + if i < 2*kd { + q.mvR(r, 1) + t.insert(q, i, k, v) + } else { + t.insert(r, 0, k, v) + } + return + } + + t.split(p, q, pi, i, k, v) +} + +// Seek returns an xenumerator positioned on a an item such that k >= item's +// key. ok reports if k == item.key The xenumerator's position is possibly +// after the last item in the tree. +func (t *xtree) Seek(k indexKey) (e *xenumerator, ok bool) { + q := t.r + if q == nil { + e = &xenumerator{nil, false, 0, k, nil, t, t.ver} + return + } + + for { + var i int + if i, ok = t.find(q, k); ok { + switch xx := q.(type) { + case *xx: + e = &xenumerator{nil, ok, 0, k, xx.xx[i].sep, t, t.ver} + return + case *xd: + e = &xenumerator{nil, ok, i, k, xx, t, t.ver} + return + } + } + switch xx := q.(type) { + case *xx: + q = xx.xx[i].ch + case *xd: + e = &xenumerator{nil, ok, i, k, xx, t, t.ver} + return + } + } +} + +// SeekFirst returns an enumerator positioned on the first KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returned and e will be nil. +func (t *xtree) SeekFirst() (e *xenumerator, err error) { + q := t.first + if q == nil { + return nil, io.EOF + } + + return &xenumerator{nil, true, 0, q.xd[0].k, q, t, t.ver}, nil +} + +// SeekLast returns an enumerator positioned on the last KV pair in the tree, +// if any. For an empty tree, err == io.EOF is returned and e will be nil. +func (t *xtree) SeekLast() (e *xenumerator, err error) { + q := t.last + if q == nil { + return nil, io.EOF + } + + return &xenumerator{nil, true, q.c - 1, q.xd[q.c-1].k, q, t, t.ver}, nil +} + +// Set sets the value associated with k. +func (t *xtree) Set(k indexKey, v int) { + pi := -1 + var p *xx + q := t.r + if q != nil { + for { + i, ok := t.find(q, k) + if ok { + switch xx := q.(type) { + case *xx: + xx.xx[i].sep.xd[0].v = v + case *xd: + xx.xd[i].v = v + } + return + } + + switch xx := q.(type) { + case *xx: + if xx.c > 2*kx { + t.splitX(p, &xx, pi, &i) + } + pi = i + p = xx + q = xx.xx[i].ch + case *xd: + switch { + case xx.c < 2*kd: + t.insert(xx, i, k, v) + default: + t.overflow(p, xx, pi, i, k, v) + } + return + } + } + } + + z := t.insert(&xd{}, 0, k, v) + t.r, t.first, t.last = z, z, z + return +} + +func (t *xtree) split(p *xx, q *xd, pi, i int, k indexKey, v int) { + t.ver++ + r := &xd{} + if q.n != nil { + r.n = q.n + r.n.p = r + } else { + t.last = r + } + q.n = r + r.p = q + + copy(r.xd[:], q.xd[kd:2*kd]) + for i := range q.xd[kd:] { + q.xd[kd+i] = zxde + } + q.c = kd + r.c = kd + if pi >= 0 { + p.insert(pi, r, r) + } else { + t.r = xnewX(q).insert(0, r, r) + } + if i > kd { + t.insert(r, i-kd, k, v) + return + } + + t.insert(q, i, k, v) +} + +func (t *xtree) splitX(p *xx, pp **xx, pi int, i *int) { + t.ver++ + q := *pp + r := &xx{} + copy(r.xx[:], q.xx[kx+1:]) + q.c = kx + r.c = kx + if pi >= 0 { + p.insert(pi, q.xx[kx].sep, r) + } else { + t.r = xnewX(q).insert(0, q.xx[kx].sep, r) + } + q.xx[kx].sep = nil + for i := range q.xx[kx+1:] { + q.xx[kx+i+1] = zxxe + } + if *i > kx { + *pp = r + *i -= kx + 1 + } +} + +func (t *xtree) underflow(p *xx, q *xd, pi int) { + t.ver++ + l, r := p.siblings(pi) + + if l != nil && l.c+q.c >= 2*kd { + l.mvR(q, 1) + } else if r != nil && q.c+r.c >= 2*kd { + q.mvL(r, 1) + r.xd[r.c] = zxde // GC + } else if l != nil { + t.cat(p, l, q, pi-1) + } else { + t.cat(p, q, r, pi) + } +} + +func (t *xtree) underflowX(p *xx, pp **xx, pi int, i *int) { + t.ver++ + var l, r *xx + q := *pp + + if pi >= 0 { + if pi > 0 { + l = p.xx[pi-1].ch.(*xx) + } + if pi < p.c { + r = p.xx[pi+1].ch.(*xx) + } + } + + if l != nil && l.c > kx { + q.xx[q.c+1].ch = q.xx[q.c].ch + copy(q.xx[1:], q.xx[:q.c]) + q.xx[0].ch = l.xx[l.c].ch + q.xx[0].sep = p.xx[pi-1].sep + q.c++ + *i++ + l.c-- + p.xx[pi-1].sep = l.xx[l.c].sep + return + } + + if r != nil && r.c > kx { + q.xx[q.c].sep = p.xx[pi].sep + q.c++ + q.xx[q.c].ch = r.xx[0].ch + p.xx[pi].sep = r.xx[0].sep + copy(r.xx[:], r.xx[1:r.c]) + r.c-- + rc := r.c + r.xx[rc].ch = r.xx[rc+1].ch + r.xx[rc].sep = nil + r.xx[rc+1].ch = nil + return + } + + if l != nil { + *i += l.c + 1 + t.catX(p, l, q, pi-1) + *pp = l + return + } + + t.catX(p, q, r, pi) +} + +// ----------------------------------------------------------------- xenumerator + +// Next returns the currently enumerated item, if it exists and moves to the +// next item in the key collation order. If there is no item to return, err == +// io.EOF is returned. +func (e *xenumerator) Next() (k indexKey, v int64, err error) { + if err = e.err; err != nil { + return + } + + if e.ver != e.t.ver { + f, hit := e.t.Seek(e.k) + if !e.hit && hit { + if err = f.next(); err != nil { + return + } + } + + *e = *f + } + if e.q == nil { + e.err, err = io.EOF, io.EOF + return + } + + if e.i >= e.q.c { + if err = e.next(); err != nil { + return + } + } + + i := e.q.xd[e.i] + k, v = i.k, int64(i.v) + e.k, e.hit = k, false + e.next() + return +} + +func (e *xenumerator) next() error { + if e.q == nil { + e.err = io.EOF + return io.EOF + } + + switch { + case e.i < e.q.c-1: + e.i++ + default: + if e.q, e.i = e.q.n, 0; e.q == nil { + e.err = io.EOF + } + } + return e.err +} + +// Prev returns the currently enumerated item, if it exists and moves to the +// previous item in the key collation order. If there is no item to return, err +// == io.EOF is returned. +func (e *xenumerator) Prev() (k indexKey, v int64, err error) { + if err = e.err; err != nil { + return + } + + if e.ver != e.t.ver { + f, hit := e.t.Seek(e.k) + if !e.hit && hit { + if err = f.prev(); err != nil { + return + } + } + + *e = *f + } + if e.q == nil { + e.err, err = io.EOF, io.EOF + return + } + + if e.i >= e.q.c { + if err = e.next(); err != nil { + return + } + } + + i := e.q.xd[e.i] + k, v = i.k, int64(i.v) + e.k, e.hit = k, false + e.prev() + return +} + +func (e *xenumerator) prev() error { + if e.q == nil { + e.err = io.EOF + return io.EOF + } + + switch { + case e.i > 0: + e.i-- + default: + if e.q = e.q.p; e.q == nil { + e.err = io.EOF + break + } + + e.i = e.q.c - 1 + } + return e.err +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/parser.go b/Godeps/_workspace/src/github.com/cznic/ql/parser.go new file mode 100644 index 000000000..1fb6e04a5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/parser.go @@ -0,0 +1,2637 @@ +// CAUTION: Generated file - DO NOT EDIT. + +// Copyright (c) 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Inital yacc source generated by ebnf2y[1] +// at 2013-10-04 23:10:47.861401015 +0200 CEST +// +// $ ebnf2y -o ql.y -oe ql.ebnf -start StatementList -pkg ql -p _ +// +// [1]: http://github.com/cznic/ebnf2y + +package ql + +import __yyfmt__ "fmt" + +import ( + "fmt" + + "github.com/cznic/mathutil" +) + +type yySymType struct { + yys int + line int + col int + item interface{} + list []interface{} +} + +type yyXError struct { + state, xsym int +} + +const ( + yyDefault = 57437 + yyEOFCode = 57344 + add = 57352 + alter = 57353 + and = 57354 + andand = 57355 + andnot = 57356 + as = 57357 + asc = 57358 + begin = 57359 + between = 57360 + bigIntType = 57361 + bigRatType = 57362 + blobType = 57363 + boolType = 57364 + by = 57365 + byteType = 57366 + column = 57367 + commit = 57368 + complex128Type = 57369 + complex64Type = 57370 + create = 57371 + defaultKwd = 57372 + deleteKwd = 57373 + desc = 57374 + distinct = 57375 + drop = 57376 + durationType = 57377 + eq = 57378 + yyErrCode = 57345 + exists = 57379 + explain = 57380 + falseKwd = 57381 + float32Type = 57383 + float64Type = 57384 + floatLit = 57346 + floatType = 57382 + from = 57385 + full = 57386 + ge = 57387 + group = 57388 + identifier = 57347 + ifKwd = 57389 + imaginaryLit = 57348 + in = 57390 + index = 57391 + insert = 57392 + int16Type = 57394 + int32Type = 57395 + int64Type = 57396 + int8Type = 57397 + intLit = 57349 + intType = 57393 + into = 57398 + is = 57399 + join = 57400 + le = 57401 + left = 57402 + like = 57403 + limit = 57404 + lsh = 57405 + neq = 57406 + not = 57407 + null = 57408 + offset = 57409 + on = 57410 + or = 57411 + order = 57412 + oror = 57413 + outer = 57414 + parseExpression = 57436 + qlParam = 57350 + right = 57415 + rollback = 57416 + rsh = 57417 + runeType = 57418 + selectKwd = 57419 + set = 57420 + stringLit = 57351 + stringType = 57421 + tableKwd = 57422 + timeType = 57423 + transaction = 57424 + trueKwd = 57425 + truncate = 57426 + uint16Type = 57428 + uint32Type = 57429 + uint64Type = 57430 + uint8Type = 57431 + uintType = 57427 + unique = 57432 + update = 57433 + values = 57434 + where = 57435 + + yyMaxDepth = 200 + yyTabOfs = -216 +) + +var ( + yyXLAT = map[int]int{ + 59: 0, // ';' (192x) + 57344: 1, // $end (191x) + 41: 2, // ')' (164x) + 43: 3, // '+' (133x) + 45: 4, // '-' (133x) + 94: 5, // '^' (133x) + 44: 6, // ',' (130x) + 40: 7, // '(' (125x) + 57347: 8, // identifier (116x) + 57409: 9, // offset (103x) + 57404: 10, // limit (101x) + 57372: 11, // defaultKwd (94x) + 57412: 12, // order (90x) + 57435: 13, // where (87x) + 57408: 14, // null (84x) + 57361: 15, // bigIntType (83x) + 57362: 16, // bigRatType (83x) + 57363: 17, // blobType (83x) + 57364: 18, // boolType (83x) + 57366: 19, // byteType (83x) + 57369: 20, // complex128Type (83x) + 57370: 21, // complex64Type (83x) + 57377: 22, // durationType (83x) + 57383: 23, // float32Type (83x) + 57384: 24, // float64Type (83x) + 57382: 25, // floatType (83x) + 57394: 26, // int16Type (83x) + 57395: 27, // int32Type (83x) + 57396: 28, // int64Type (83x) + 57397: 29, // int8Type (83x) + 57393: 30, // intType (83x) + 57418: 31, // runeType (83x) + 57421: 32, // stringType (83x) + 57423: 33, // timeType (83x) + 57428: 34, // uint16Type (83x) + 57429: 35, // uint32Type (83x) + 57430: 36, // uint64Type (83x) + 57431: 37, // uint8Type (83x) + 57427: 38, // uintType (83x) + 57381: 39, // falseKwd (81x) + 57346: 40, // floatLit (81x) + 57388: 41, // group (81x) + 57348: 42, // imaginaryLit (81x) + 57349: 43, // intLit (81x) + 57407: 44, // not (81x) + 57411: 45, // or (81x) + 57413: 46, // oror (81x) + 57350: 47, // qlParam (81x) + 57351: 48, // stringLit (81x) + 57425: 49, // trueKwd (81x) + 33: 50, // '!' (77x) + 57385: 51, // from (75x) + 57358: 52, // asc (71x) + 57374: 53, // desc (71x) + 93: 54, // ']' (70x) + 57357: 55, // as (69x) + 58: 56, // ':' (67x) + 57354: 57, // and (67x) + 57355: 58, // andand (65x) + 124: 59, // '|' (56x) + 57360: 60, // between (54x) + 57390: 61, // in (54x) + 60: 62, // '<' (53x) + 62: 63, // '>' (53x) + 57378: 64, // eq (53x) + 57387: 65, // ge (53x) + 57399: 66, // is (53x) + 57401: 67, // le (53x) + 57403: 68, // like (53x) + 57406: 69, // neq (53x) + 57513: 70, // Type (52x) + 57453: 71, // Conversion (51x) + 57483: 72, // Literal (51x) + 57484: 73, // Operand (51x) + 57488: 74, // PrimaryExpression (51x) + 57491: 75, // QualifiedIdent (51x) + 42: 76, // '*' (48x) + 57514: 77, // UnaryExpr (47x) + 37: 78, // '%' (44x) + 38: 79, // '&' (44x) + 47: 80, // '/' (44x) + 57356: 81, // andnot (44x) + 57405: 82, // lsh (44x) + 57417: 83, // rsh (44x) + 57490: 84, // PrimaryTerm (40x) + 57489: 85, // PrimaryFactor (36x) + 91: 86, // '[' (31x) + 57470: 87, // Factor (25x) + 57471: 88, // Factor1 (25x) + 57511: 89, // Term (24x) + 57467: 90, // Expression (23x) + 57519: 91, // logOr (16x) + 57446: 92, // ColumnName (10x) + 57386: 93, // full (10x) + 57402: 94, // left (10x) + 57415: 95, // right (10x) + 57419: 96, // selectKwd (10x) + 57510: 97, // TableName (9x) + 57449: 98, // CommaOpt (7x) + 57468: 99, // ExpressionList (7x) + 57410: 100, // on (7x) + 57497: 101, // SelectStmt (7x) + 57400: 102, // join (6x) + 57443: 103, // Call (5x) + 57376: 104, // drop (5x) + 57476: 105, // Index (5x) + 57506: 106, // Slice (5x) + 57445: 107, // ColumnDef (4x) + 57379: 108, // exists (4x) + 57389: 109, // ifKwd (4x) + 57391: 110, // index (4x) + 57414: 111, // outer (4x) + 57422: 112, // tableKwd (4x) + 57434: 113, // values (4x) + 57353: 114, // alter (3x) + 57438: 115, // AlterTableStmt (3x) + 57359: 116, // begin (3x) + 57442: 117, // BeginTransactionStmt (3x) + 57368: 118, // commit (3x) + 57450: 119, // CommitStmt (3x) + 57371: 120, // create (3x) + 57455: 121, // CreateIndexStmt (3x) + 57457: 122, // CreateTableStmt (3x) + 57461: 123, // DeleteFromStmt (3x) + 57373: 124, // deleteKwd (3x) + 57463: 125, // DropIndexStmt (3x) + 57464: 126, // DropTableStmt (3x) + 57465: 127, // EmptyStmt (3x) + 57380: 128, // explain (3x) + 57466: 129, // ExplainStmt (3x) + 57392: 130, // insert (3x) + 57477: 131, // InsertIntoStmt (3x) + 57492: 132, // RecordSet (3x) + 57493: 133, // RecordSet1 (3x) + 57416: 134, // rollback (3x) + 57496: 135, // RollbackStmt (3x) + 57520: 136, // semiOpt (3x) + 57508: 137, // Statement (3x) + 57426: 138, // truncate (3x) + 57512: 139, // TruncateTableStmt (3x) + 57433: 140, // update (3x) + 57515: 141, // UpdateStmt (3x) + 57517: 142, // WhereClause (3x) + 61: 143, // '=' (2x) + 57352: 144, // add (2x) + 57439: 145, // Assignment (2x) + 57365: 146, // by (2x) + 57447: 147, // ColumnNameList (2x) + 57458: 148, // CreateTableStmt1 (2x) + 57472: 149, // Field (2x) + 57518: 150, // logAnd (2x) + 57420: 151, // set (2x) + 46: 152, // '.' (1x) + 57440: 153, // AssignmentList (1x) + 57441: 154, // AssignmentList1 (1x) + 57444: 155, // Call1 (1x) + 57367: 156, // column (1x) + 57448: 157, // ColumnNameList1 (1x) + 57451: 158, // Constraint (1x) + 57452: 159, // ConstraintOpt (1x) + 57454: 160, // CreateIndexIfNotExists (1x) + 57456: 161, // CreateIndexStmtUnique (1x) + 57459: 162, // Default (1x) + 57460: 163, // DefaultOpt (1x) + 57375: 164, // distinct (1x) + 57462: 165, // DropIndexIfExists (1x) + 57469: 166, // ExpressionList1 (1x) + 57473: 167, // Field1 (1x) + 57474: 168, // FieldList (1x) + 57475: 169, // GroupByClause (1x) + 57478: 170, // InsertIntoStmt1 (1x) + 57479: 171, // InsertIntoStmt2 (1x) + 57398: 172, // into (1x) + 57480: 173, // JoinClause (1x) + 57481: 174, // JoinClauseOpt (1x) + 57482: 175, // JoinType (1x) + 57485: 176, // OrderBy (1x) + 57486: 177, // OrderBy1 (1x) + 57487: 178, // OuterOpt (1x) + 57436: 179, // parseExpression (1x) + 57494: 180, // RecordSet2 (1x) + 57495: 181, // RecordSetList (1x) + 57498: 182, // SelectStmtDistinct (1x) + 57499: 183, // SelectStmtFieldList (1x) + 57500: 184, // SelectStmtGroup (1x) + 57501: 185, // SelectStmtLimit (1x) + 57502: 186, // SelectStmtOffset (1x) + 57503: 187, // SelectStmtOrder (1x) + 57504: 188, // SelectStmtWhere (1x) + 57505: 189, // SetOpt (1x) + 57507: 190, // Start (1x) + 57509: 191, // StatementList (1x) + 57424: 192, // transaction (1x) + 57432: 193, // unique (1x) + 57516: 194, // UpdateStmt1 (1x) + 57437: 195, // $default (0x) + 57345: 196, // error (0x) + } + + yySymNames = []string{ + "';'", + "$end", + "')'", + "'+'", + "'-'", + "'^'", + "','", + "'('", + "identifier", + "offset", + "limit", + "defaultKwd", + "order", + "where", + "null", + "bigIntType", + "bigRatType", + "blobType", + "boolType", + "byteType", + "complex128Type", + "complex64Type", + "durationType", + "float32Type", + "float64Type", + "floatType", + "int16Type", + "int32Type", + "int64Type", + "int8Type", + "intType", + "runeType", + "stringType", + "timeType", + "uint16Type", + "uint32Type", + "uint64Type", + "uint8Type", + "uintType", + "falseKwd", + "floatLit", + "group", + "imaginaryLit", + "intLit", + "not", + "or", + "oror", + "qlParam", + "stringLit", + "trueKwd", + "'!'", + "from", + "asc", + "desc", + "']'", + "as", + "':'", + "and", + "andand", + "'|'", + "between", + "in", + "'<'", + "'>'", + "eq", + "ge", + "is", + "le", + "like", + "neq", + "Type", + "Conversion", + "Literal", + "Operand", + "PrimaryExpression", + "QualifiedIdent", + "'*'", + "UnaryExpr", + "'%'", + "'&'", + "'/'", + "andnot", + "lsh", + "rsh", + "PrimaryTerm", + "PrimaryFactor", + "'['", + "Factor", + "Factor1", + "Term", + "Expression", + "logOr", + "ColumnName", + "full", + "left", + "right", + "selectKwd", + "TableName", + "CommaOpt", + "ExpressionList", + "on", + "SelectStmt", + "join", + "Call", + "drop", + "Index", + "Slice", + "ColumnDef", + "exists", + "ifKwd", + "index", + "outer", + "tableKwd", + "values", + "alter", + "AlterTableStmt", + "begin", + "BeginTransactionStmt", + "commit", + "CommitStmt", + "create", + "CreateIndexStmt", + "CreateTableStmt", + "DeleteFromStmt", + "deleteKwd", + "DropIndexStmt", + "DropTableStmt", + "EmptyStmt", + "explain", + "ExplainStmt", + "insert", + "InsertIntoStmt", + "RecordSet", + "RecordSet1", + "rollback", + "RollbackStmt", + "semiOpt", + "Statement", + "truncate", + "TruncateTableStmt", + "update", + "UpdateStmt", + "WhereClause", + "'='", + "add", + "Assignment", + "by", + "ColumnNameList", + "CreateTableStmt1", + "Field", + "logAnd", + "set", + "'.'", + "AssignmentList", + "AssignmentList1", + "Call1", + "column", + "ColumnNameList1", + "Constraint", + "ConstraintOpt", + "CreateIndexIfNotExists", + "CreateIndexStmtUnique", + "Default", + "DefaultOpt", + "distinct", + "DropIndexIfExists", + "ExpressionList1", + "Field1", + "FieldList", + "GroupByClause", + "InsertIntoStmt1", + "InsertIntoStmt2", + "into", + "JoinClause", + "JoinClauseOpt", + "JoinType", + "OrderBy", + "OrderBy1", + "OuterOpt", + "parseExpression", + "RecordSet2", + "RecordSetList", + "SelectStmtDistinct", + "SelectStmtFieldList", + "SelectStmtGroup", + "SelectStmtLimit", + "SelectStmtOffset", + "SelectStmtOrder", + "SelectStmtWhere", + "SetOpt", + "Start", + "StatementList", + "transaction", + "unique", + "UpdateStmt1", + "$default", + "error", + } + + yyReductions = map[int]struct{ xsym, components int }{ + 0: {0, 1}, + 1: {190, 1}, + 2: {190, 2}, + 3: {115, 5}, + 4: {115, 6}, + 5: {145, 3}, + 6: {153, 3}, + 7: {154, 0}, + 8: {154, 3}, + 9: {117, 2}, + 10: {103, 3}, + 11: {103, 3}, + 12: {155, 0}, + 13: {155, 1}, + 14: {107, 4}, + 15: {92, 1}, + 16: {147, 3}, + 17: {157, 0}, + 18: {157, 3}, + 19: {119, 1}, + 20: {158, 2}, + 21: {158, 1}, + 22: {159, 0}, + 23: {159, 1}, + 24: {71, 4}, + 25: {121, 10}, + 26: {160, 0}, + 27: {160, 3}, + 28: {161, 0}, + 29: {161, 1}, + 30: {122, 8}, + 31: {122, 11}, + 32: {148, 0}, + 33: {148, 3}, + 34: {162, 2}, + 35: {163, 0}, + 36: {163, 1}, + 37: {123, 3}, + 38: {123, 4}, + 39: {125, 4}, + 40: {165, 0}, + 41: {165, 2}, + 42: {126, 3}, + 43: {126, 5}, + 44: {127, 0}, + 45: {129, 2}, + 46: {90, 1}, + 47: {90, 3}, + 48: {91, 1}, + 49: {91, 1}, + 50: {99, 3}, + 51: {166, 0}, + 52: {166, 3}, + 53: {87, 1}, + 54: {87, 5}, + 55: {87, 6}, + 56: {87, 6}, + 57: {87, 7}, + 58: {87, 5}, + 59: {87, 6}, + 60: {87, 3}, + 61: {87, 4}, + 62: {88, 1}, + 63: {88, 3}, + 64: {88, 3}, + 65: {88, 3}, + 66: {88, 3}, + 67: {88, 3}, + 68: {88, 3}, + 69: {88, 3}, + 70: {149, 2}, + 71: {167, 0}, + 72: {167, 2}, + 73: {168, 1}, + 74: {168, 3}, + 75: {169, 3}, + 76: {105, 3}, + 77: {131, 10}, + 78: {131, 5}, + 79: {170, 0}, + 80: {170, 3}, + 81: {171, 0}, + 82: {171, 5}, + 83: {72, 1}, + 84: {72, 1}, + 85: {72, 1}, + 86: {72, 1}, + 87: {72, 1}, + 88: {72, 1}, + 89: {72, 1}, + 90: {73, 1}, + 91: {73, 1}, + 92: {73, 1}, + 93: {73, 3}, + 94: {176, 4}, + 95: {177, 0}, + 96: {177, 1}, + 97: {177, 1}, + 98: {74, 1}, + 99: {74, 1}, + 100: {74, 2}, + 101: {74, 2}, + 102: {74, 2}, + 103: {85, 1}, + 104: {85, 3}, + 105: {85, 3}, + 106: {85, 3}, + 107: {85, 3}, + 108: {84, 1}, + 109: {84, 3}, + 110: {84, 3}, + 111: {84, 3}, + 112: {84, 3}, + 113: {84, 3}, + 114: {84, 3}, + 115: {84, 3}, + 116: {75, 1}, + 117: {75, 3}, + 118: {132, 2}, + 119: {133, 1}, + 120: {133, 4}, + 121: {136, 0}, + 122: {136, 1}, + 123: {180, 0}, + 124: {180, 2}, + 125: {181, 1}, + 126: {181, 3}, + 127: {135, 1}, + 128: {175, 1}, + 129: {175, 1}, + 130: {175, 1}, + 131: {178, 0}, + 132: {178, 1}, + 133: {173, 6}, + 134: {174, 0}, + 135: {174, 1}, + 136: {101, 12}, + 137: {185, 0}, + 138: {185, 2}, + 139: {186, 0}, + 140: {186, 2}, + 141: {182, 0}, + 142: {182, 1}, + 143: {183, 1}, + 144: {183, 1}, + 145: {183, 2}, + 146: {188, 0}, + 147: {188, 1}, + 148: {184, 0}, + 149: {184, 1}, + 150: {187, 0}, + 151: {187, 1}, + 152: {106, 3}, + 153: {106, 4}, + 154: {106, 4}, + 155: {106, 5}, + 156: {137, 1}, + 157: {137, 1}, + 158: {137, 1}, + 159: {137, 1}, + 160: {137, 1}, + 161: {137, 1}, + 162: {137, 1}, + 163: {137, 1}, + 164: {137, 1}, + 165: {137, 1}, + 166: {137, 1}, + 167: {137, 1}, + 168: {137, 1}, + 169: {137, 1}, + 170: {137, 1}, + 171: {191, 1}, + 172: {191, 3}, + 173: {97, 1}, + 174: {89, 1}, + 175: {89, 3}, + 176: {150, 1}, + 177: {150, 1}, + 178: {139, 3}, + 179: {70, 1}, + 180: {70, 1}, + 181: {70, 1}, + 182: {70, 1}, + 183: {70, 1}, + 184: {70, 1}, + 185: {70, 1}, + 186: {70, 1}, + 187: {70, 1}, + 188: {70, 1}, + 189: {70, 1}, + 190: {70, 1}, + 191: {70, 1}, + 192: {70, 1}, + 193: {70, 1}, + 194: {70, 1}, + 195: {70, 1}, + 196: {70, 1}, + 197: {70, 1}, + 198: {70, 1}, + 199: {70, 1}, + 200: {70, 1}, + 201: {70, 1}, + 202: {70, 1}, + 203: {141, 5}, + 204: {194, 0}, + 205: {194, 1}, + 206: {77, 1}, + 207: {77, 2}, + 208: {77, 2}, + 209: {77, 2}, + 210: {77, 2}, + 211: {142, 2}, + 212: {189, 0}, + 213: {189, 1}, + 214: {98, 0}, + 215: {98, 1}, + } + + yyXErrors = map[yyXError]string{ + yyXError{1, -1}: "expected $end", + yyXError{43, -1}: "expected '('", + yyXError{157, -1}: "expected '('", + yyXError{181, -1}: "expected '('", + yyXError{281, -1}: "expected '('", + yyXError{309, -1}: "expected '('", + yyXError{313, -1}: "expected '('", + yyXError{344, -1}: "expected '('", + yyXError{118, -1}: "expected ')'", + yyXError{119, -1}: "expected ')'", + yyXError{120, -1}: "expected ')'", + yyXError{187, -1}: "expected ')'", + yyXError{189, -1}: "expected ')'", + yyXError{190, -1}: "expected ')'", + yyXError{194, -1}: "expected ')'", + yyXError{196, -1}: "expected ')'", + yyXError{265, -1}: "expected ')'", + yyXError{279, -1}: "expected ')'", + yyXError{284, -1}: "expected ')'", + yyXError{290, -1}: "expected ')'", + yyXError{318, -1}: "expected ')'", + yyXError{335, -1}: "expected ')'", + yyXError{346, -1}: "expected ')'", + yyXError{36, -1}: "expected '='", + yyXError{233, -1}: "expected BY", + yyXError{236, -1}: "expected BY", + yyXError{352, -1}: "expected COLUMN", + yyXError{7, -1}: "expected CREATE INDEX optional UNIQUE clause or one of [INDEX, TABLE, UNIQUE]", + yyXError{337, -1}: "expected CREATE INDEX statement optional IF NOT EXISTS cluse or one of [IF, identifier]", + yyXError{316, -1}: "expected CREATE TABLE statement colum definition list or optional comma or one of [')', ',']", + yyXError{333, -1}: "expected CREATE TABLE statement colum definition list or optional comma or one of [')', ',']", + yyXError{293, -1}: "expected DROP INDEX statement optional IF EXISTS clause or one of [IF, identifier]", + yyXError{296, -1}: "expected EXISTS", + yyXError{300, -1}: "expected EXISTS", + yyXError{311, -1}: "expected EXISTS", + yyXError{340, -1}: "expected EXISTS", + yyXError{8, -1}: "expected FROM", + yyXError{215, -1}: "expected FROM", + yyXError{216, -1}: "expected FROM", + yyXError{306, -1}: "expected INDEX", + yyXError{307, -1}: "expected INDEX", + yyXError{276, -1}: "expected INSERT INTO statement optional column list clause or SELECT statement or one of ['(', SELECT, VALUES]", + yyXError{285, -1}: "expected INSERT INTO statement optional values list or optional comma or one of [$end, ',', ';']", + yyXError{11, -1}: "expected INTO", + yyXError{257, -1}: "expected JOIN", + yyXError{258, -1}: "expected JOIN", + yyXError{310, -1}: "expected NOT", + yyXError{339, -1}: "expected NOT", + yyXError{176, -1}: "expected NULL", + yyXError{324, -1}: "expected NULL", + yyXError{260, -1}: "expected ON", + yyXError{342, -1}: "expected ON", + yyXError{246, -1}: "expected ORDER BY clause optional collation specification or one of [$end, ')', ';', ASC, DESC, LIMIT, OFFSET]", + yyXError{217, -1}: "expected RecordSetList or one of ['(', identifier]", + yyXError{13, -1}: "expected SELECT statement field list or SELECT statement optional DISTINCT clause or one of ['!', '(', '*', '+', '-', '^', DISTINCT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{210, -1}: "expected SELECT statement field list or one of ['!', '(', '*', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{224, -1}: "expected SELECT statement optional GROUP BY clause or SELECT statement optional JOIN clause or SELECT statement optional LIMIT clause or SELECT statement optional OFFSET clause or SELECT statement optional ORDER BY clause or SELECT statement optional WHERE clause or one of [$end, ')', ';', FULL, GROUP, LEFT, LIMIT, OFFSET, ORDER, RIGHT, WHERE]", + yyXError{222, -1}: "expected SELECT statement optional GROUP BY clause or SELECT statement optional JOIN clause or SELECT statement optional LIMIT clause or SELECT statement optional OFFSET clause or SELECT statement optional ORDER BY clause or SELECT statement optional WHERE clause or optional comma or one of [$end, ')', ',', ';', FULL, GROUP, LEFT, LIMIT, OFFSET, ORDER, RIGHT, WHERE]", + yyXError{230, -1}: "expected SELECT statement optional GROUP BY clause or SELECT statement optional LIMIT clause or SELECT statement optional OFFSET clause or SELECT statement optional ORDER BY clause or SELECT statement optional WHERE clause or one of [$end, ')', ';', GROUP, LIMIT, OFFSET, ORDER, WHERE]", + yyXError{231, -1}: "expected SELECT statement optional GROUP BY clause or SELECT statement optional LIMIT clause or SELECT statement optional OFFSET clause or SELECT statement optional ORDER BY clause or one of [$end, ')', ';', GROUP, LIMIT, OFFSET, ORDER]", + yyXError{234, -1}: "expected SELECT statement optional LIMIT clause or SELECT statement optional OFFSET clause or SELECT statement optional ORDER BY clause or one of [$end, ')', ';', LIMIT, OFFSET, ORDER]", + yyXError{237, -1}: "expected SELECT statement optional LIMIT clause or SELECT statement optional OFFSET clause or one of [$end, ')', ';', LIMIT, OFFSET]", + yyXError{239, -1}: "expected SELECT statement optional OFFSET clause or one of [$end, ')', ';', OFFSET]", + yyXError{220, -1}: "expected SELECT statement or SELECT", + yyXError{186, -1}: "expected SELECT statement or expression list or one of ['!', '(', '+', '-', '^', NULL, QL parameter, SELECT, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{193, -1}: "expected SELECT statement or expression list or one of ['!', '(', '+', '-', '^', NULL, QL parameter, SELECT, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{277, -1}: "expected SELECT statement or one of [SELECT, VALUES]", + yyXError{33, -1}: "expected SetOpt or assignment list or one of [SET, identifier]", + yyXError{0, -1}: "expected Start or one of [$end, ';', ALTER, BEGIN, COMMIT, CREATE, DELETE, DROP, EXPLAIN, INSERT, ROLLBACK, SELECT, TRUNCATE, UPDATE, parse expression prefix]", + yyXError{4, -1}: "expected TABLE", + yyXError{30, -1}: "expected TABLE", + yyXError{5, -1}: "expected TRANSACTION", + yyXError{39, -1}: "expected UPDATE statement optional WHERE clause or one of [$end, ';', WHERE]", + yyXError{304, -1}: "expected WHERE clause or one of [$end, ';', WHERE]", + yyXError{37, -1}: "expected assignment list optional trailing comma or optional comma or one of [$end, ',', ';', WHERE]", + yyXError{34, -1}: "expected assignment list or identifier", + yyXError{204, -1}: "expected assignment or one of [$end, ';', WHERE, identifier]", + yyXError{250, -1}: "expected column name list or identifier", + yyXError{278, -1}: "expected column name list or identifier", + yyXError{251, -1}: "expected column name list with optional trailing comma or optional comma or one of [$end, ')', ',', ';', LIMIT, OFFSET, ORDER]", + yyXError{353, -1}: "expected column name or identifier", + yyXError{255, -1}: "expected column name or one of [$end, ')', ';', LIMIT, OFFSET, ORDER, identifier]", + yyXError{109, -1}: "expected expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{121, -1}: "expected expression list expression or logical or operator or optional comma or one of [$end, ')', ',', ';', ASC, DESC, LIMIT, OFFSET, OR, ||]", + yyXError{245, -1}: "expected expression list or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{283, -1}: "expected expression list or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{289, -1}: "expected expression list or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{345, -1}: "expected expression list or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{124, -1}: "expected expression or one of [$end, '!', '(', ')', '+', '-', ';', '^', ASC, DESC, LIMIT, NULL, OFFSET, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{96, -1}: "expected expression or one of ['!', '(', '+', '-', ':', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{101, -1}: "expected expression or one of ['!', '(', '+', '-', ']', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{114, -1}: "expected expression or one of ['!', '(', '+', '-', ']', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{3, -1}: "expected expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{42, -1}: "expected expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{58, -1}: "expected expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{199, -1}: "expected expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{206, -1}: "expected expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{240, -1}: "expected expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{243, -1}: "expected expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{261, -1}: "expected expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{329, -1}: "expected expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{104, -1}: "expected expression term or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{212, -1}: "expected field expression optional AS clause or logical or operator or one of [',', AS, FROM, OR, ||]", + yyXError{270, -1}: "expected field expression or one of ['!', '(', '+', '-', '^', FROM, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{95, -1}: "expected function call optional argument list or one of ['!', '(', ')', '*', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{61, -1}: "expected function call or string index or string slice or one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{94, -1}: "expected function call or string index or string slice or one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{128, -1}: "expected function call or string index or string slice or one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{129, -1}: "expected function call or string index or string slice or one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{130, -1}: "expected function call or string index or string slice or one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{35, -1}: "expected identifier", + yyXError{131, -1}: "expected identifier", + yyXError{268, -1}: "expected identifier", + yyXError{273, -1}: "expected identifier", + yyXError{299, -1}: "expected identifier", + yyXError{301, -1}: "expected identifier", + yyXError{338, -1}: "expected identifier", + yyXError{341, -1}: "expected identifier", + yyXError{343, -1}: "expected identifier", + yyXError{44, -1}: "expected logical and operator or one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{108, -1}: "expected logical and operator or one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{125, -1}: "expected logical or operator or one of [$end, ')', ',', ';', ASC, DESC, LIMIT, OFFSET, OR, ||]", + yyXError{325, -1}: "expected logical or operator or one of [$end, ')', ',', ';', DEFAULT, OR, ||]", + yyXError{331, -1}: "expected logical or operator or one of [$end, ')', ',', ';', OR, ||]", + yyXError{262, -1}: "expected logical or operator or one of [$end, ')', ';', GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{45, -1}: "expected logical or operator or one of [$end, ')', ';', GROUP, LIMIT, OFFSET, OR, ORDER, ||]", + yyXError{241, -1}: "expected logical or operator or one of [$end, ')', ';', OFFSET, OR, ||]", + yyXError{244, -1}: "expected logical or operator or one of [$end, ')', ';', OR, ||]", + yyXError{207, -1}: "expected logical or operator or one of [$end, ',', ';', OR, WHERE, ||]", + yyXError{356, -1}: "expected logical or operator or one of [$end, OR, ||]", + yyXError{147, -1}: "expected logical or operator or one of [')', OR, ||]", + yyXError{200, -1}: "expected logical or operator or one of [')', OR, ||]", + yyXError{100, -1}: "expected logical or operator or one of [':', ']', OR, ||]", + yyXError{102, -1}: "expected logical or operator or one of [']', OR, ||]", + yyXError{115, -1}: "expected logical or operator or one of [']', OR, ||]", + yyXError{64, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{48, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{49, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{50, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{51, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{52, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{53, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{54, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{55, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{56, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{57, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{59, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{60, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{97, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{98, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{99, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{103, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{107, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{113, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{116, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{117, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{126, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{127, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{132, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{148, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{201, -1}: "expected one of [!=, $end, &&, &^, '%', '&', '(', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', '[', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{62, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{63, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{140, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{141, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{142, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{143, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{144, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{145, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{146, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{153, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{154, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{155, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{156, -1}: "expected one of [!=, $end, &&, &^, '%', '&', ')', '*', '+', ',', '-', '/', ':', ';', '<', '>', ']', '^', '|', <<, <=, ==, >=, >>, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{47, -1}: "expected one of [!=, $end, &&, ')', '+', ',', '-', ':', ';', '<', '>', ']', '^', '|', <=, ==, >=, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{168, -1}: "expected one of [!=, $end, &&, ')', '+', ',', '-', ':', ';', '<', '>', ']', '^', '|', <=, ==, >=, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{169, -1}: "expected one of [!=, $end, &&, ')', '+', ',', '-', ':', ';', '<', '>', ']', '^', '|', <=, ==, >=, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{170, -1}: "expected one of [!=, $end, &&, ')', '+', ',', '-', ':', ';', '<', '>', ']', '^', '|', <=, ==, >=, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{171, -1}: "expected one of [!=, $end, &&, ')', '+', ',', '-', ':', ';', '<', '>', ']', '^', '|', <=, ==, >=, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{172, -1}: "expected one of [!=, $end, &&, ')', '+', ',', '-', ':', ';', '<', '>', ']', '^', '|', <=, ==, >=, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{173, -1}: "expected one of [!=, $end, &&, ')', '+', ',', '-', ':', ';', '<', '>', ']', '^', '|', <=, ==, >=, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{174, -1}: "expected one of [!=, $end, &&, ')', '+', ',', '-', ':', ';', '<', '>', ']', '^', '|', <=, ==, >=, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{46, -1}: "expected one of [!=, $end, &&, ')', ',', ':', ';', '<', '>', ']', <=, ==, >=, AND, AS, ASC, BETWEEN, DEFAULT, DESC, FROM, GROUP, IN, IS, LIKE, LIMIT, NOT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{180, -1}: "expected one of [$end, &&, ')', '+', ',', '-', ':', ';', ']', '^', '|', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{185, -1}: "expected one of [$end, &&, ')', '+', ',', '-', ':', ';', ']', '^', '|', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{65, -1}: "expected one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{112, -1}: "expected one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{175, -1}: "expected one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{177, -1}: "expected one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{191, -1}: "expected one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{192, -1}: "expected one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{197, -1}: "expected one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{198, -1}: "expected one of [$end, &&, ')', ',', ':', ';', ']', AND, AS, ASC, DEFAULT, DESC, FROM, GROUP, LIMIT, OFFSET, OR, ORDER, WHERE, ||]", + yyXError{66, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{67, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{68, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{69, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{70, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{71, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{72, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{73, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{74, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{75, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{76, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{77, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{78, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{79, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{80, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{81, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{82, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{83, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{84, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{85, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{86, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{87, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{88, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{89, -1}: "expected one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{32, -1}: "expected one of [$end, '(', ';', ADD, DROP, SELECT, SET, VALUES, WHERE, identifier]", + yyXError{288, -1}: "expected one of [$end, '(', ';']", + yyXError{38, -1}: "expected one of [$end, ')', ',', ';', '=', LIMIT, OFFSET, ORDER, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, float, float32, float64, int, int16, int32, int64, int8, rune, string, time, uint, uint16, uint32, uint64, uint8]", + yyXError{219, -1}: "expected one of [$end, ')', ',', ';', AS, FULL, GROUP, LEFT, LIMIT, OFFSET, ON, ORDER, RIGHT, WHERE]", + yyXError{266, -1}: "expected one of [$end, ')', ',', ';', AS, FULL, GROUP, LEFT, LIMIT, OFFSET, ON, ORDER, RIGHT, WHERE]", + yyXError{326, -1}: "expected one of [$end, ')', ',', ';', DEFAULT]", + yyXError{327, -1}: "expected one of [$end, ')', ',', ';', DEFAULT]", + yyXError{267, -1}: "expected one of [$end, ')', ',', ';', FULL, GROUP, LEFT, LIMIT, OFFSET, ON, ORDER, RIGHT, WHERE]", + yyXError{269, -1}: "expected one of [$end, ')', ',', ';', FULL, GROUP, LEFT, LIMIT, OFFSET, ON, ORDER, RIGHT, WHERE]", + yyXError{221, -1}: "expected one of [$end, ')', ',', ';', FULL, GROUP, LEFT, LIMIT, OFFSET, ORDER, RIGHT, WHERE]", + yyXError{263, -1}: "expected one of [$end, ')', ',', ';', FULL, GROUP, LEFT, LIMIT, OFFSET, ORDER, RIGHT, WHERE]", + yyXError{256, -1}: "expected one of [$end, ')', ',', ';', LIMIT, OFFSET, ORDER]", + yyXError{328, -1}: "expected one of [$end, ')', ',', ';']", + yyXError{330, -1}: "expected one of [$end, ')', ',', ';']", + yyXError{123, -1}: "expected one of [$end, ')', ';', ASC, DESC, LIMIT, OFFSET]", + yyXError{229, -1}: "expected one of [$end, ')', ';', GROUP, LIMIT, OFFSET, ORDER, WHERE]", + yyXError{232, -1}: "expected one of [$end, ')', ';', GROUP, LIMIT, OFFSET, ORDER]", + yyXError{235, -1}: "expected one of [$end, ')', ';', LIMIT, OFFSET, ORDER]", + yyXError{252, -1}: "expected one of [$end, ')', ';', LIMIT, OFFSET, ORDER]", + yyXError{254, -1}: "expected one of [$end, ')', ';', LIMIT, OFFSET, ORDER]", + yyXError{238, -1}: "expected one of [$end, ')', ';', LIMIT, OFFSET]", + yyXError{247, -1}: "expected one of [$end, ')', ';', LIMIT, OFFSET]", + yyXError{248, -1}: "expected one of [$end, ')', ';', LIMIT, OFFSET]", + yyXError{249, -1}: "expected one of [$end, ')', ';', LIMIT, OFFSET]", + yyXError{242, -1}: "expected one of [$end, ')', ';']", + yyXError{205, -1}: "expected one of [$end, ',', ';', WHERE]", + yyXError{291, -1}: "expected one of [$end, ',', ';']", + yyXError{203, -1}: "expected one of [$end, ';', WHERE]", + yyXError{2, -1}: "expected one of [$end, ';']", + yyXError{6, -1}: "expected one of [$end, ';']", + yyXError{12, -1}: "expected one of [$end, ';']", + yyXError{14, -1}: "expected one of [$end, ';']", + yyXError{15, -1}: "expected one of [$end, ';']", + yyXError{16, -1}: "expected one of [$end, ';']", + yyXError{17, -1}: "expected one of [$end, ';']", + yyXError{18, -1}: "expected one of [$end, ';']", + yyXError{19, -1}: "expected one of [$end, ';']", + yyXError{20, -1}: "expected one of [$end, ';']", + yyXError{21, -1}: "expected one of [$end, ';']", + yyXError{22, -1}: "expected one of [$end, ';']", + yyXError{23, -1}: "expected one of [$end, ';']", + yyXError{24, -1}: "expected one of [$end, ';']", + yyXError{25, -1}: "expected one of [$end, ';']", + yyXError{26, -1}: "expected one of [$end, ';']", + yyXError{27, -1}: "expected one of [$end, ';']", + yyXError{28, -1}: "expected one of [$end, ';']", + yyXError{29, -1}: "expected one of [$end, ';']", + yyXError{40, -1}: "expected one of [$end, ';']", + yyXError{41, -1}: "expected one of [$end, ';']", + yyXError{209, -1}: "expected one of [$end, ';']", + yyXError{282, -1}: "expected one of [$end, ';']", + yyXError{287, -1}: "expected one of [$end, ';']", + yyXError{292, -1}: "expected one of [$end, ';']", + yyXError{295, -1}: "expected one of [$end, ';']", + yyXError{298, -1}: "expected one of [$end, ';']", + yyXError{302, -1}: "expected one of [$end, ';']", + yyXError{305, -1}: "expected one of [$end, ';']", + yyXError{321, -1}: "expected one of [$end, ';']", + yyXError{336, -1}: "expected one of [$end, ';']", + yyXError{347, -1}: "expected one of [$end, ';']", + yyXError{348, -1}: "expected one of [$end, ';']", + yyXError{354, -1}: "expected one of [$end, ';']", + yyXError{355, -1}: "expected one of [$end, ';']", + yyXError{358, -1}: "expected one of [$end, ';']", + yyXError{211, -1}: "expected one of ['!', '(', '*', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{105, -1}: "expected one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{106, -1}: "expected one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{110, -1}: "expected one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{111, -1}: "expected one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{320, -1}: "expected one of [')', ',']", + yyXError{178, -1}: "expected one of ['+', '-', '^', '|', AND]", + yyXError{183, -1}: "expected one of ['+', '-', '^', '|', AND]", + yyXError{213, -1}: "expected one of [',', FROM]", + yyXError{214, -1}: "expected one of [',', FROM]", + yyXError{271, -1}: "expected one of [',', FROM]", + yyXError{272, -1}: "expected one of [',', FROM]", + yyXError{274, -1}: "expected one of [',', FROM]", + yyXError{350, -1}: "expected one of [ADD, DROP]", + yyXError{158, -1}: "expected one of [BETWEEN, IN]", + yyXError{9, -1}: "expected one of [INDEX, TABLE]", + yyXError{225, -1}: "expected one of [JOIN, OUTER]", + yyXError{226, -1}: "expected one of [JOIN, OUTER]", + yyXError{227, -1}: "expected one of [JOIN, OUTER]", + yyXError{160, -1}: "expected one of [NOT, NULL]", + yyXError{280, -1}: "expected one of [SELECT, VALUES]", + yyXError{323, -1}: "expected optional DEFAULT clause or one of [$end, ')', ',', ';', DEFAULT]", + yyXError{322, -1}: "expected optional DEFAULT clause or optional column value constraint or one of [$end, '!', '(', ')', '+', ',', '-', ';', '^', DEFAULT, NOT, NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{228, -1}: "expected optional OUTER clause or one of [JOIN, OUTER]", + yyXError{122, -1}: "expected optional comma or one of [$end, ')', ',', ';', ASC, DESC, LIMIT, OFFSET]", + yyXError{253, -1}: "expected optional comma or one of [$end, ')', ',', ';', LIMIT, OFFSET, ORDER]", + yyXError{202, -1}: "expected optional comma or one of [$end, ',', ';', WHERE]", + yyXError{286, -1}: "expected optional comma or one of [$end, ',', ';']", + yyXError{317, -1}: "expected optional comma or one of [')', ',']", + yyXError{334, -1}: "expected optional comma or one of [')', ',']", + yyXError{159, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{161, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{162, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{163, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{164, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{165, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{166, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{167, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{179, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{182, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{184, -1}: "expected primary expression factor or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{90, -1}: "expected primary expression or one of ['(', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{91, -1}: "expected primary expression or one of ['(', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{92, -1}: "expected primary expression or one of ['(', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{93, -1}: "expected primary expression or one of ['(', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{149, -1}: "expected primary expression term or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{150, -1}: "expected primary expression term or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{151, -1}: "expected primary expression term or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{152, -1}: "expected primary expression term or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{218, -1}: "expected record set optional AS clause or one of [$end, ')', ',', ';', AS, FULL, GROUP, LEFT, LIMIT, OFFSET, ON, ORDER, RIGHT, WHERE]", + yyXError{223, -1}: "expected record set or one of [$end, '(', ')', ';', FULL, GROUP, LEFT, LIMIT, OFFSET, ORDER, RIGHT, WHERE, identifier]", + yyXError{259, -1}: "expected record set or one of ['(', identifier]", + yyXError{188, -1}: "expected semiOpt or one of [')', ';']", + yyXError{195, -1}: "expected semiOpt or one of [')', ';']", + yyXError{264, -1}: "expected semiOpt or one of [')', ';']", + yyXError{10, -1}: "expected statement or one of [$end, ';', ALTER, BEGIN, COMMIT, CREATE, DELETE, DROP, EXPLAIN, INSERT, ROLLBACK, SELECT, TRUNCATE, UPDATE]", + yyXError{357, -1}: "expected statement or one of [$end, ';', ALTER, BEGIN, COMMIT, CREATE, DELETE, DROP, EXPLAIN, INSERT, ROLLBACK, SELECT, TRUNCATE, UPDATE]", + yyXError{314, -1}: "expected table column definition or identifier", + yyXError{332, -1}: "expected table column definition or identifier", + yyXError{351, -1}: "expected table column definition or identifier", + yyXError{319, -1}: "expected table column definition or one of [')', identifier]", + yyXError{31, -1}: "expected table name or identifier", + yyXError{208, -1}: "expected table name or identifier", + yyXError{275, -1}: "expected table name or identifier", + yyXError{297, -1}: "expected table name or identifier", + yyXError{303, -1}: "expected table name or identifier", + yyXError{312, -1}: "expected table name or identifier", + yyXError{349, -1}: "expected table name or identifier", + yyXError{294, -1}: "expected table name or one of [IF, identifier]", + yyXError{308, -1}: "expected table name or one of [IF, identifier]", + yyXError{315, -1}: "expected type or one of [bigint, bigrat, blob, bool, byte, complex128, complex64, duration, float, float32, float64, int, int16, int32, int64, int8, rune, string, time, uint, uint16, uint32, uint64, uint8]", + yyXError{133, -1}: "expected unary expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{134, -1}: "expected unary expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{135, -1}: "expected unary expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{136, -1}: "expected unary expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{137, -1}: "expected unary expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{138, -1}: "expected unary expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + yyXError{139, -1}: "expected unary expression or one of ['!', '(', '+', '-', '^', NULL, QL parameter, bigint, bigrat, blob, bool, byte, complex128, complex64, duration, false, float, float32, float64, floating-point literal, identifier, imaginary literal, int, int16, int32, int64, int8, integer literal, rune, string, string literal, time, true, uint, uint16, uint32, uint64, uint8]", + } + + yyParseTab = [359][]uint16{ + // 0 + {172, 172, 96: 229, 101: 242, 104: 225, 114: 220, 231, 221, 232, 222, 233, 223, 234, 235, 236, 224, 237, 238, 230, 226, 239, 227, 240, 134: 228, 241, 137: 245, 246, 243, 247, 244, 179: 219, 190: 217, 218}, + {1: 216}, + {573, 215}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 572}, + {112: 565}, + // 5 + {192: 564}, + {197, 197}, + {110: 188, 112: 524, 161: 522, 193: 523}, + {51: 519}, + {110: 509, 112: 510}, + // 10 + {172, 172, 96: 229, 101: 242, 104: 225, 114: 220, 231, 221, 232, 222, 233, 223, 234, 235, 236, 224, 237, 238, 230, 226, 239, 227, 240, 134: 228, 241, 137: 508, 246, 243, 247, 244}, + {172: 491}, + {89, 89}, + {3: 75, 75, 75, 7: 75, 75, 14: 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 42: 75, 75, 47: 75, 75, 75, 75, 76: 75, 164: 427, 182: 426}, + {60, 60}, + // 15 + {59, 59}, + {58, 58}, + {57, 57}, + {56, 56}, + {55, 55}, + // 20 + {54, 54}, + {53, 53}, + {52, 52}, + {51, 51}, + {50, 50}, + // 25 + {49, 49}, + {48, 48}, + {47, 47}, + {46, 46}, + {45, 45}, + // 30 + {112: 424}, + {8: 248, 97: 249}, + {43, 43, 7: 43, 43, 13: 43, 96: 43, 104: 43, 113: 43, 144: 43, 151: 43}, + {8: 4, 151: 251, 189: 250}, + {8: 254, 92: 252, 145: 253, 153: 255}, + // 35 + {8: 3}, + {143: 422}, + {209, 209, 6: 209, 13: 209, 154: 418}, + {201, 201, 201, 6: 201, 9: 201, 201, 12: 201, 15: 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 143: 201}, + {12, 12, 13: 258, 142: 257, 194: 256}, + // 40 + {13, 13}, + {11, 11}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 261}, + {7: 415}, + {170, 170, 170, 6: 170, 9: 170, 170, 170, 170, 170, 41: 170, 45: 170, 170, 51: 170, 170, 170, 170, 170, 170, 327, 326, 150: 325}, + // 45 + {5, 5, 5, 9: 5, 5, 12: 5, 41: 5, 45: 322, 321, 91: 320}, + {163, 163, 163, 6: 163, 9: 163, 163, 163, 163, 163, 41: 163, 44: 374, 163, 163, 51: 163, 163, 163, 163, 163, 163, 163, 163, 60: 375, 373, 380, 378, 382, 377, 376, 379, 383, 381}, + {154, 154, 154, 368, 367, 365, 154, 9: 154, 154, 154, 154, 154, 41: 154, 44: 154, 154, 154, 51: 154, 154, 154, 154, 154, 154, 154, 154, 366, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154}, + {133, 133, 133, 133, 133, 133, 133, 133, 9: 133, 133, 133, 133, 133, 41: 133, 44: 133, 133, 133, 51: 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 76: 133, 78: 133, 133, 133, 133, 133, 133, 86: 133}, + {132, 132, 132, 132, 132, 132, 132, 132, 9: 132, 132, 132, 132, 132, 41: 132, 44: 132, 132, 132, 51: 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 76: 132, 78: 132, 132, 132, 132, 132, 132, 86: 132}, + // 50 + {131, 131, 131, 131, 131, 131, 131, 131, 9: 131, 131, 131, 131, 131, 41: 131, 44: 131, 131, 131, 51: 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 76: 131, 78: 131, 131, 131, 131, 131, 131, 86: 131}, + {130, 130, 130, 130, 130, 130, 130, 130, 9: 130, 130, 130, 130, 130, 41: 130, 44: 130, 130, 130, 51: 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 76: 130, 78: 130, 130, 130, 130, 130, 130, 86: 130}, + {129, 129, 129, 129, 129, 129, 129, 129, 9: 129, 129, 129, 129, 129, 41: 129, 44: 129, 129, 129, 51: 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 76: 129, 78: 129, 129, 129, 129, 129, 129, 86: 129}, + {128, 128, 128, 128, 128, 128, 128, 128, 9: 128, 128, 128, 128, 128, 41: 128, 44: 128, 128, 128, 51: 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 76: 128, 78: 128, 128, 128, 128, 128, 128, 86: 128}, + {127, 127, 127, 127, 127, 127, 127, 127, 9: 127, 127, 127, 127, 127, 41: 127, 44: 127, 127, 127, 51: 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 76: 127, 78: 127, 127, 127, 127, 127, 127, 86: 127}, + // 55 + {126, 126, 126, 126, 126, 126, 126, 126, 9: 126, 126, 126, 126, 126, 41: 126, 44: 126, 126, 126, 51: 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 76: 126, 78: 126, 126, 126, 126, 126, 126, 86: 126}, + {125, 125, 125, 125, 125, 125, 125, 125, 9: 125, 125, 125, 125, 125, 41: 125, 44: 125, 125, 125, 51: 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 76: 125, 78: 125, 125, 125, 125, 125, 125, 86: 125}, + {124, 124, 124, 124, 124, 124, 124, 124, 9: 124, 124, 124, 124, 124, 41: 124, 44: 124, 124, 124, 51: 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 76: 124, 78: 124, 124, 124, 124, 124, 124, 86: 124}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 363}, + {118, 118, 118, 118, 118, 118, 118, 118, 9: 118, 118, 118, 118, 118, 41: 118, 44: 118, 118, 118, 51: 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 76: 118, 78: 118, 118, 118, 118, 118, 118, 86: 118}, + // 60 + {117, 117, 117, 117, 117, 117, 117, 117, 9: 117, 117, 117, 117, 117, 41: 117, 44: 117, 117, 117, 51: 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 76: 117, 78: 117, 117, 117, 117, 117, 117, 86: 117}, + {10, 10, 10, 10, 10, 10, 10, 311, 9: 10, 10, 10, 10, 10, 41: 10, 44: 10, 10, 10, 51: 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 76: 10, 78: 10, 10, 10, 10, 10, 10, 86: 312, 103: 315, 105: 313, 314}, + {113, 113, 113, 113, 113, 113, 113, 9: 113, 113, 113, 113, 113, 41: 113, 44: 113, 113, 113, 51: 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 76: 355, 78: 353, 350, 354, 349, 351, 352}, + {108, 108, 108, 108, 108, 108, 108, 9: 108, 108, 108, 108, 108, 41: 108, 44: 108, 108, 108, 51: 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 76: 108, 78: 108, 108, 108, 108, 108, 108}, + {100, 100, 100, 100, 100, 100, 100, 100, 9: 100, 100, 100, 100, 100, 41: 100, 44: 100, 100, 100, 51: 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 76: 100, 78: 100, 100, 100, 100, 100, 100, 86: 100, 152: 347}, + // 65 + {42, 42, 42, 6: 42, 9: 42, 42, 42, 42, 42, 41: 42, 45: 42, 42, 51: 42, 42, 42, 42, 42, 42, 42, 42}, + {37, 37, 37, 37, 37, 37, 37, 37, 37, 11: 37, 14: 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 42: 37, 37, 37, 47: 37, 37, 37, 37}, + {36, 36, 36, 36, 36, 36, 36, 36, 36, 11: 36, 14: 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42: 36, 36, 36, 47: 36, 36, 36, 36}, + {35, 35, 35, 35, 35, 35, 35, 35, 35, 11: 35, 14: 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 42: 35, 35, 35, 47: 35, 35, 35, 35}, + {34, 34, 34, 34, 34, 34, 34, 34, 34, 11: 34, 14: 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 42: 34, 34, 34, 47: 34, 34, 34, 34}, + // 70 + {33, 33, 33, 33, 33, 33, 33, 33, 33, 11: 33, 14: 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 42: 33, 33, 33, 47: 33, 33, 33, 33}, + {32, 32, 32, 32, 32, 32, 32, 32, 32, 11: 32, 14: 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 42: 32, 32, 32, 47: 32, 32, 32, 32}, + {31, 31, 31, 31, 31, 31, 31, 31, 31, 11: 31, 14: 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 42: 31, 31, 31, 47: 31, 31, 31, 31}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 11: 30, 14: 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 42: 30, 30, 30, 47: 30, 30, 30, 30}, + {29, 29, 29, 29, 29, 29, 29, 29, 29, 11: 29, 14: 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 42: 29, 29, 29, 47: 29, 29, 29, 29}, + // 75 + {28, 28, 28, 28, 28, 28, 28, 28, 28, 11: 28, 14: 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 42: 28, 28, 28, 47: 28, 28, 28, 28}, + {27, 27, 27, 27, 27, 27, 27, 27, 27, 11: 27, 14: 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 42: 27, 27, 27, 47: 27, 27, 27, 27}, + {26, 26, 26, 26, 26, 26, 26, 26, 26, 11: 26, 14: 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 42: 26, 26, 26, 47: 26, 26, 26, 26}, + {25, 25, 25, 25, 25, 25, 25, 25, 25, 11: 25, 14: 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 42: 25, 25, 25, 47: 25, 25, 25, 25}, + {24, 24, 24, 24, 24, 24, 24, 24, 24, 11: 24, 14: 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 42: 24, 24, 24, 47: 24, 24, 24, 24}, + // 80 + {23, 23, 23, 23, 23, 23, 23, 23, 23, 11: 23, 14: 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 42: 23, 23, 23, 47: 23, 23, 23, 23}, + {22, 22, 22, 22, 22, 22, 22, 22, 22, 11: 22, 14: 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 42: 22, 22, 22, 47: 22, 22, 22, 22}, + {21, 21, 21, 21, 21, 21, 21, 21, 21, 11: 21, 14: 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 42: 21, 21, 21, 47: 21, 21, 21, 21}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 11: 20, 14: 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 42: 20, 20, 20, 47: 20, 20, 20, 20}, + {19, 19, 19, 19, 19, 19, 19, 19, 19, 11: 19, 14: 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 42: 19, 19, 19, 47: 19, 19, 19, 19}, + // 85 + {18, 18, 18, 18, 18, 18, 18, 18, 18, 11: 18, 14: 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 42: 18, 18, 18, 47: 18, 18, 18, 18}, + {17, 17, 17, 17, 17, 17, 17, 17, 17, 11: 17, 14: 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 42: 17, 17, 17, 47: 17, 17, 17, 17}, + {16, 16, 16, 16, 16, 16, 16, 16, 16, 11: 16, 14: 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 42: 16, 16, 16, 47: 16, 16, 16, 16}, + {15, 15, 15, 15, 15, 15, 15, 15, 15, 11: 15, 14: 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 42: 15, 15, 15, 47: 15, 15, 15, 15}, + {14, 14, 14, 14, 14, 14, 14, 14, 14, 11: 14, 14: 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 42: 14, 14, 14, 47: 14, 14, 14, 14}, + // 90 + {7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 70: 259, 276, 271, 275, 346, 273}, + {7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 70: 259, 276, 271, 275, 345, 273}, + {7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 70: 259, 276, 271, 275, 344, 273}, + {7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 70: 259, 276, 271, 275, 310, 273}, + {6, 6, 6, 6, 6, 6, 6, 311, 9: 6, 6, 6, 6, 6, 41: 6, 44: 6, 6, 6, 51: 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 76: 6, 78: 6, 6, 6, 6, 6, 6, 86: 312, 103: 315, 105: 313, 314}, + // 95 + {2: 204, 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 335, 279, 84: 278, 263, 87: 281, 262, 260, 337, 99: 336, 155: 334}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 56: 317, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 316}, + {116, 116, 116, 116, 116, 116, 116, 116, 9: 116, 116, 116, 116, 116, 41: 116, 44: 116, 116, 116, 51: 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 76: 116, 78: 116, 116, 116, 116, 116, 116, 86: 116}, + {115, 115, 115, 115, 115, 115, 115, 115, 9: 115, 115, 115, 115, 115, 41: 115, 44: 115, 115, 115, 51: 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 76: 115, 78: 115, 115, 115, 115, 115, 115, 86: 115}, + {114, 114, 114, 114, 114, 114, 114, 114, 9: 114, 114, 114, 114, 114, 41: 114, 44: 114, 114, 114, 51: 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 76: 114, 78: 114, 114, 114, 114, 114, 114, 86: 114}, + // 100 + {45: 322, 321, 54: 329, 56: 330, 91: 320}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 54: 319, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 318}, + {45: 322, 321, 54: 323, 91: 320}, + {64, 64, 64, 64, 64, 64, 64, 64, 9: 64, 64, 64, 64, 64, 41: 64, 44: 64, 64, 64, 51: 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 76: 64, 78: 64, 64, 64, 64, 64, 64, 86: 64}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 324}, + // 105 + {3: 168, 168, 168, 7: 168, 168, 14: 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 42: 168, 168, 47: 168, 168, 168, 168}, + {3: 167, 167, 167, 7: 167, 167, 14: 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 42: 167, 167, 47: 167, 167, 167, 167}, + {63, 63, 63, 63, 63, 63, 63, 63, 9: 63, 63, 63, 63, 63, 41: 63, 44: 63, 63, 63, 51: 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 76: 63, 78: 63, 63, 63, 63, 63, 63, 86: 63}, + {169, 169, 169, 6: 169, 9: 169, 169, 169, 169, 169, 41: 169, 45: 169, 169, 51: 169, 169, 169, 169, 169, 169, 327, 326, 150: 325}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 328, 262}, + // 110 + {3: 40, 40, 40, 7: 40, 40, 14: 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 42: 40, 40, 47: 40, 40, 40, 40}, + {3: 39, 39, 39, 7: 39, 39, 14: 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 42: 39, 39, 47: 39, 39, 39, 39}, + {41, 41, 41, 6: 41, 9: 41, 41, 41, 41, 41, 41: 41, 45: 41, 41, 51: 41, 41, 41, 41, 41, 41, 41, 41}, + {140, 140, 140, 140, 140, 140, 140, 140, 9: 140, 140, 140, 140, 140, 41: 140, 44: 140, 140, 140, 51: 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 76: 140, 78: 140, 140, 140, 140, 140, 140, 86: 140}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 54: 332, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 331}, + // 115 + {45: 322, 321, 54: 333, 91: 320}, + {62, 62, 62, 62, 62, 62, 62, 62, 9: 62, 62, 62, 62, 62, 41: 62, 44: 62, 62, 62, 51: 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 76: 62, 78: 62, 62, 62, 62, 62, 62, 86: 62}, + {61, 61, 61, 61, 61, 61, 61, 61, 9: 61, 61, 61, 61, 61, 41: 61, 44: 61, 61, 61, 51: 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 76: 61, 78: 61, 61, 61, 61, 61, 61, 86: 61}, + {2: 343}, + {2: 342}, + // 120 + {2: 203}, + {165, 165, 165, 6: 165, 9: 165, 165, 45: 322, 321, 52: 165, 165, 91: 320, 166: 338}, + {2, 2, 2, 6: 340, 9: 2, 2, 52: 2, 2, 98: 339}, + {166, 166, 166, 9: 166, 166, 52: 166, 166}, + {1, 1, 1, 309, 308, 306, 7: 274, 280, 1, 1, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 52: 1, 1, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 341}, + // 125 + {164, 164, 164, 6: 164, 9: 164, 164, 45: 322, 321, 52: 164, 164, 91: 320}, + {205, 205, 205, 205, 205, 205, 205, 205, 9: 205, 205, 205, 205, 205, 41: 205, 44: 205, 205, 205, 51: 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 76: 205, 78: 205, 205, 205, 205, 205, 205, 86: 205}, + {206, 206, 206, 206, 206, 206, 206, 206, 9: 206, 206, 206, 206, 206, 41: 206, 44: 206, 206, 206, 51: 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 76: 206, 78: 206, 206, 206, 206, 206, 206, 86: 206}, + {7, 7, 7, 7, 7, 7, 7, 311, 9: 7, 7, 7, 7, 7, 41: 7, 44: 7, 7, 7, 51: 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 76: 7, 78: 7, 7, 7, 7, 7, 7, 86: 312, 103: 315, 105: 313, 314}, + {8, 8, 8, 8, 8, 8, 8, 311, 9: 8, 8, 8, 8, 8, 41: 8, 44: 8, 8, 8, 51: 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 76: 8, 78: 8, 8, 8, 8, 8, 8, 86: 312, 103: 315, 105: 313, 314}, + // 130 + {9, 9, 9, 9, 9, 9, 9, 311, 9: 9, 9, 9, 9, 9, 41: 9, 44: 9, 9, 9, 51: 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 76: 9, 78: 9, 9, 9, 9, 9, 9, 86: 312, 103: 315, 105: 313, 314}, + {8: 348}, + {99, 99, 99, 99, 99, 99, 99, 99, 9: 99, 99, 99, 99, 99, 41: 99, 44: 99, 99, 99, 51: 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 76: 99, 78: 99, 99, 99, 99, 99, 99, 86: 99}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 362}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 361}, + // 135 + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 360}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 359}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 358}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 357}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 356}, + // 140 + {101, 101, 101, 101, 101, 101, 101, 9: 101, 101, 101, 101, 101, 41: 101, 44: 101, 101, 101, 51: 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 76: 101, 78: 101, 101, 101, 101, 101, 101}, + {102, 102, 102, 102, 102, 102, 102, 9: 102, 102, 102, 102, 102, 41: 102, 44: 102, 102, 102, 51: 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 76: 102, 78: 102, 102, 102, 102, 102, 102}, + {103, 103, 103, 103, 103, 103, 103, 9: 103, 103, 103, 103, 103, 41: 103, 44: 103, 103, 103, 51: 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 76: 103, 78: 103, 103, 103, 103, 103, 103}, + {104, 104, 104, 104, 104, 104, 104, 9: 104, 104, 104, 104, 104, 41: 104, 44: 104, 104, 104, 51: 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 76: 104, 78: 104, 104, 104, 104, 104, 104}, + {105, 105, 105, 105, 105, 105, 105, 9: 105, 105, 105, 105, 105, 41: 105, 44: 105, 105, 105, 51: 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 76: 105, 78: 105, 105, 105, 105, 105, 105}, + // 145 + {106, 106, 106, 106, 106, 106, 106, 9: 106, 106, 106, 106, 106, 41: 106, 44: 106, 106, 106, 51: 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 76: 106, 78: 106, 106, 106, 106, 106, 106}, + {107, 107, 107, 107, 107, 107, 107, 9: 107, 107, 107, 107, 107, 41: 107, 44: 107, 107, 107, 51: 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 76: 107, 78: 107, 107, 107, 107, 107, 107}, + {2: 364, 45: 322, 321, 91: 320}, + {123, 123, 123, 123, 123, 123, 123, 123, 9: 123, 123, 123, 123, 123, 41: 123, 44: 123, 123, 123, 51: 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 76: 123, 78: 123, 123, 123, 123, 123, 123, 86: 123}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 372}, + // 150 + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 371}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 370}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 369}, + {109, 109, 109, 109, 109, 109, 109, 9: 109, 109, 109, 109, 109, 41: 109, 44: 109, 109, 109, 51: 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 76: 355, 78: 353, 350, 354, 349, 351, 352}, + {110, 110, 110, 110, 110, 110, 110, 9: 110, 110, 110, 110, 110, 41: 110, 44: 110, 110, 110, 51: 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 76: 355, 78: 353, 350, 354, 349, 351, 352}, + // 155 + {111, 111, 111, 111, 111, 111, 111, 9: 111, 111, 111, 111, 111, 41: 111, 44: 111, 111, 111, 51: 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 76: 355, 78: 353, 350, 354, 349, 351, 352}, + {112, 112, 112, 112, 112, 112, 112, 9: 112, 112, 112, 112, 112, 41: 112, 44: 112, 112, 112, 51: 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 76: 355, 78: 353, 350, 354, 349, 351, 352}, + {7: 409}, + {60: 398, 397}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 394}, + // 160 + {14: 391, 44: 392}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 390}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 389}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 388}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 387}, + // 165 + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 386}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 385}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 384}, + {147, 147, 147, 368, 367, 365, 147, 9: 147, 147, 147, 147, 147, 41: 147, 44: 147, 147, 147, 51: 147, 147, 147, 147, 147, 147, 147, 147, 366, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147}, + {148, 148, 148, 368, 367, 365, 148, 9: 148, 148, 148, 148, 148, 41: 148, 44: 148, 148, 148, 51: 148, 148, 148, 148, 148, 148, 148, 148, 366, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148}, + // 170 + {149, 149, 149, 368, 367, 365, 149, 9: 149, 149, 149, 149, 149, 41: 149, 44: 149, 149, 149, 51: 149, 149, 149, 149, 149, 149, 149, 149, 366, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149}, + {150, 150, 150, 368, 367, 365, 150, 9: 150, 150, 150, 150, 150, 41: 150, 44: 150, 150, 150, 51: 150, 150, 150, 150, 150, 150, 150, 150, 366, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150}, + {151, 151, 151, 368, 367, 365, 151, 9: 151, 151, 151, 151, 151, 41: 151, 44: 151, 151, 151, 51: 151, 151, 151, 151, 151, 151, 151, 151, 366, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151}, + {152, 152, 152, 368, 367, 365, 152, 9: 152, 152, 152, 152, 152, 41: 152, 44: 152, 152, 152, 51: 152, 152, 152, 152, 152, 152, 152, 152, 366, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152}, + {153, 153, 153, 368, 367, 365, 153, 9: 153, 153, 153, 153, 153, 41: 153, 44: 153, 153, 153, 51: 153, 153, 153, 153, 153, 153, 153, 153, 366, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153}, + // 175 + {156, 156, 156, 6: 156, 9: 156, 156, 156, 156, 156, 41: 156, 45: 156, 156, 51: 156, 156, 156, 156, 156, 156, 156, 156}, + {14: 393}, + {155, 155, 155, 6: 155, 9: 155, 155, 155, 155, 155, 41: 155, 45: 155, 155, 51: 155, 155, 155, 155, 155, 155, 155, 155}, + {3: 368, 367, 365, 57: 395, 59: 366}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 396}, + // 180 + {158, 158, 158, 368, 367, 365, 158, 9: 158, 158, 158, 158, 158, 41: 158, 45: 158, 158, 51: 158, 158, 158, 158, 158, 158, 158, 158, 366}, + {7: 402}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 399}, + {3: 368, 367, 365, 57: 400, 59: 366}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 401}, + // 185 + {157, 157, 157, 368, 367, 365, 157, 9: 157, 157, 157, 157, 157, 41: 157, 45: 157, 157, 51: 157, 157, 157, 157, 157, 157, 157, 157, 366}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 337, 96: 229, 99: 403, 101: 404}, + {2: 408}, + {406, 2: 95, 136: 405}, + {2: 407}, + // 190 + {2: 94}, + {159, 159, 159, 6: 159, 9: 159, 159, 159, 159, 159, 41: 159, 45: 159, 159, 51: 159, 159, 159, 159, 159, 159, 159, 159}, + {161, 161, 161, 6: 161, 9: 161, 161, 161, 161, 161, 41: 161, 45: 161, 161, 51: 161, 161, 161, 161, 161, 161, 161, 161}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 337, 96: 229, 99: 410, 101: 411}, + {2: 414}, + // 195 + {406, 2: 95, 136: 412}, + {2: 413}, + {160, 160, 160, 6: 160, 9: 160, 160, 160, 160, 160, 41: 160, 45: 160, 160, 51: 160, 160, 160, 160, 160, 160, 160, 160}, + {162, 162, 162, 6: 162, 9: 162, 162, 162, 162, 162, 41: 162, 45: 162, 162, 51: 162, 162, 162, 162, 162, 162, 162, 162}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 416}, + // 200 + {2: 417, 45: 322, 321, 91: 320}, + {192, 192, 192, 192, 192, 192, 192, 192, 9: 192, 192, 192, 192, 192, 41: 192, 44: 192, 192, 192, 51: 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 76: 192, 78: 192, 192, 192, 192, 192, 192, 86: 192}, + {2, 2, 6: 420, 13: 2, 98: 419}, + {210, 210, 13: 210}, + {1, 1, 8: 254, 13: 1, 92: 252, 145: 421}, + // 205 + {208, 208, 6: 208, 13: 208}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 423}, + {211, 211, 6: 211, 13: 211, 45: 322, 321, 91: 320}, + {8: 248, 97: 425}, + {38, 38}, + // 210 + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 432, 279, 84: 278, 263, 87: 281, 262, 260, 428, 149: 429, 168: 430, 183: 431}, + {3: 74, 74, 74, 7: 74, 74, 14: 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 42: 74, 74, 47: 74, 74, 74, 74, 76: 74}, + {6: 145, 45: 322, 321, 51: 145, 55: 489, 91: 320, 167: 488}, + {6: 143, 51: 143}, + {6: 486, 51: 72}, + // 215 + {51: 433}, + {51: 73}, + {7: 436, 435, 132: 437, 434, 181: 438}, + {93, 93, 93, 6: 93, 9: 93, 93, 12: 93, 93, 41: 93, 55: 484, 93: 93, 93, 93, 100: 93, 180: 483}, + {97, 97, 97, 6: 97, 9: 97, 97, 12: 97, 97, 41: 97, 55: 97, 93: 97, 97, 97, 100: 97}, + // 220 + {96: 229, 101: 480}, + {91, 91, 91, 6: 91, 9: 91, 91, 12: 91, 91, 41: 91, 93: 91, 91, 91}, + {2, 2, 2, 6: 439, 9: 2, 2, 12: 2, 2, 41: 2, 93: 2, 2, 2, 98: 440}, + {1, 1, 1, 7: 436, 435, 1, 1, 12: 1, 1, 41: 1, 93: 1, 1, 1, 132: 479, 434}, + {82, 82, 82, 9: 82, 82, 12: 82, 82, 41: 82, 93: 443, 441, 442, 173: 445, 446, 444}, + // 225 + {102: 88, 111: 88}, + {102: 87, 111: 87}, + {102: 86, 111: 86}, + {102: 85, 111: 473, 178: 474}, + {81, 81, 81, 9: 81, 81, 12: 81, 81, 41: 81}, + // 230 + {70, 70, 70, 9: 70, 70, 12: 70, 258, 41: 70, 142: 448, 188: 447}, + {68, 68, 68, 9: 68, 68, 12: 68, 41: 449, 169: 451, 184: 450}, + {69, 69, 69, 9: 69, 69, 12: 69, 41: 69}, + {146: 466}, + {66, 66, 66, 9: 66, 66, 12: 452, 176: 454, 187: 453}, + // 235 + {67, 67, 67, 9: 67, 67, 12: 67}, + {146: 461}, + {79, 79, 79, 9: 79, 456, 185: 455}, + {65, 65, 65, 9: 65, 65}, + {77, 77, 77, 9: 459, 186: 458}, + // 240 + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 457}, + {78, 78, 78, 9: 78, 45: 322, 321, 91: 320}, + {80, 80, 80}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 460}, + {76, 76, 76, 45: 322, 321, 91: 320}, + // 245 + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 337, 99: 462}, + {121, 121, 121, 9: 121, 121, 52: 464, 465, 177: 463}, + {122, 122, 122, 9: 122, 122}, + {120, 120, 120, 9: 120, 120}, + {119, 119, 119, 9: 119, 119}, + // 250 + {8: 254, 92: 467, 147: 468}, + {199, 199, 199, 6: 199, 9: 199, 199, 12: 199, 157: 469}, + {141, 141, 141, 9: 141, 141, 12: 141}, + {2, 2, 2, 6: 471, 9: 2, 2, 12: 2, 98: 470}, + {200, 200, 200, 9: 200, 200, 12: 200}, + // 255 + {1, 1, 1, 8: 254, 1, 1, 12: 1, 92: 472}, + {198, 198, 198, 6: 198, 9: 198, 198, 12: 198}, + {102: 84}, + {102: 475}, + {7: 436, 435, 132: 476, 434}, + // 260 + {100: 477}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 478}, + {83, 83, 83, 9: 83, 83, 12: 83, 83, 41: 83, 45: 322, 321, 91: 320}, + {90, 90, 90, 6: 90, 9: 90, 90, 12: 90, 90, 41: 90, 93: 90, 90, 90}, + {406, 2: 95, 136: 481}, + // 265 + {2: 482}, + {96, 96, 96, 6: 96, 9: 96, 96, 12: 96, 96, 41: 96, 55: 96, 93: 96, 96, 96, 100: 96}, + {98, 98, 98, 6: 98, 9: 98, 98, 12: 98, 98, 41: 98, 93: 98, 98, 98, 100: 98}, + {8: 485}, + {92, 92, 92, 6: 92, 9: 92, 92, 12: 92, 92, 41: 92, 93: 92, 92, 92, 100: 92}, + // 270 + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 71, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 428, 149: 487}, + {6: 142, 51: 142}, + {6: 146, 51: 146}, + {8: 490}, + {6: 144, 51: 144}, + // 275 + {8: 248, 97: 492}, + {7: 494, 96: 137, 113: 137, 170: 493}, + {96: 229, 101: 498, 113: 497}, + {8: 254, 92: 467, 147: 495}, + {2: 496}, + // 280 + {96: 136, 113: 136}, + {7: 499}, + {138, 138}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 337, 99: 500}, + {2: 501}, + // 285 + {135, 135, 6: 135, 171: 502}, + {2, 2, 6: 504, 98: 503}, + {139, 139}, + {1, 1, 7: 505}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 337, 99: 506}, + // 290 + {2: 507}, + {134, 134, 6: 134}, + {171, 171}, + {8: 176, 109: 516, 165: 515}, + {8: 248, 97: 511, 109: 512}, + // 295 + {174, 174}, + {108: 513}, + {8: 248, 97: 514}, + {173, 173}, + {8: 518}, + // 300 + {108: 517}, + {8: 175}, + {177, 177}, + {8: 248, 97: 520}, + {179, 179, 13: 258, 142: 521}, + // 305 + {178, 178}, + {110: 553}, + {110: 187}, + {8: 248, 97: 525, 109: 526}, + {7: 548}, + // 310 + {44: 527}, + {108: 528}, + {8: 248, 97: 529}, + {7: 530}, + {8: 254, 92: 531, 107: 532}, + // 315 + {15: 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 70: 538}, + {2: 184, 6: 184, 148: 533}, + {2: 2, 6: 535, 98: 534}, + {2: 537}, + {2: 1, 8: 254, 92: 531, 107: 536}, + // 320 + {2: 183, 6: 183}, + {185, 185}, + {194, 194, 194, 309, 308, 306, 194, 274, 280, 11: 194, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 540, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 541, 158: 542, 539}, + {181, 181, 181, 6: 181, 11: 545, 162: 546, 544}, + {14: 543}, + // 325 + {195, 195, 195, 6: 195, 11: 195, 45: 322, 321, 91: 320}, + {193, 193, 193, 6: 193, 11: 193}, + {196, 196, 196, 6: 196, 11: 196}, + {202, 202, 202, 6: 202}, + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 547}, + // 330 + {180, 180, 180, 6: 180}, + {182, 182, 182, 6: 182, 45: 322, 321, 91: 320}, + {8: 254, 92: 531, 107: 549}, + {2: 184, 6: 184, 148: 550}, + {2: 2, 6: 535, 98: 551}, + // 335 + {2: 552}, + {186, 186}, + {8: 190, 109: 555, 160: 554}, + {8: 558}, + {44: 556}, + // 340 + {108: 557}, + {8: 189}, + {100: 559}, + {8: 560}, + {7: 561}, + // 345 + {3: 309, 308, 306, 7: 274, 280, 14: 265, 282, 283, 284, 285, 286, 287, 288, 289, 291, 292, 290, 294, 295, 296, 297, 293, 298, 299, 300, 302, 303, 304, 305, 301, 264, 267, 42: 268, 269, 47: 272, 270, 266, 307, 70: 259, 276, 271, 275, 277, 273, 77: 279, 84: 278, 263, 87: 281, 262, 260, 337, 99: 562}, + {2: 563}, + {191, 191}, + {207, 207}, + {8: 248, 97: 566}, + // 350 + {104: 568, 144: 567}, + {8: 254, 92: 531, 107: 571}, + {156: 569}, + {8: 254, 92: 570}, + {212, 212}, + // 355 + {213, 213}, + {1: 214, 45: 322, 321, 91: 320}, + {172, 172, 96: 229, 101: 242, 104: 225, 114: 220, 231, 221, 232, 222, 233, 223, 234, 235, 236, 224, 237, 238, 230, 226, 239, 227, 240, 134: 228, 241, 137: 574, 246, 243, 247, 244}, + {44, 44}, + } +) + +var yyDebug = 0 + +type yyLexer interface { + Lex(lval *yySymType) int + Error(s string) +} + +type yyLexerEx interface { + yyLexer + Reduced(rule, state int, lval *yySymType) bool +} + +func yySymName(c int) (s string) { + x, ok := yyXLAT[c] + if ok { + return yySymNames[x] + } + + return __yyfmt__.Sprintf("%d", c) +} + +func yylex1(yylex yyLexer, lval *yySymType) (n int) { + n = yylex.Lex(lval) + if n <= 0 { + n = yyEOFCode + } + if yyDebug >= 3 { + __yyfmt__.Printf("\nlex %s(%#x %d), lval: %+v\n", yySymName(n), n, n, lval) + } + return n +} + +func yyParse(yylex yyLexer) int { + const yyError = 196 + + yyEx, _ := yylex.(yyLexerEx) + var yyn int + var yylval yySymType + var yyVAL yySymType + yyS := make([]yySymType, 200) + + Nerrs := 0 /* number of errors */ + Errflag := 0 /* error recovery flag */ + yyerrok := func() { + if yyDebug >= 2 { + __yyfmt__.Printf("yyerrok()\n") + } + Errflag = 0 + } + _ = yyerrok + yystate := 0 + yychar := -1 + var yyxchar int + var yyshift int + yyp := -1 + goto yystack + +ret0: + return 0 + +ret1: + return 1 + +yystack: + /* put a state and value onto the stack */ + yyp++ + if yyp >= len(yyS) { + nyys := make([]yySymType, len(yyS)*2) + copy(nyys, yyS) + yyS = nyys + } + yyS[yyp] = yyVAL + yyS[yyp].yys = yystate + +yynewstate: + if yychar < 0 { + yychar = yylex1(yylex, &yylval) + var ok bool + if yyxchar, ok = yyXLAT[yychar]; !ok { + yyxchar = len(yySymNames) // > tab width + } + } + if yyDebug >= 4 { + var a []int + for _, v := range yyS[:yyp+1] { + a = append(a, v.yys) + } + __yyfmt__.Printf("state stack %v\n", a) + } + row := yyParseTab[yystate] + yyn = 0 + if yyxchar < len(row) { + if yyn = int(row[yyxchar]); yyn != 0 { + yyn += yyTabOfs + } + } + switch { + case yyn > 0: // shift + yychar = -1 + yyVAL = yylval + yystate = yyn + yyshift = yyn + if yyDebug >= 2 { + __yyfmt__.Printf("shift, and goto state %d\n", yystate) + } + if Errflag > 0 { + Errflag-- + } + goto yystack + case yyn < 0: // reduce + case yystate == 1: // accept + if yyDebug >= 2 { + __yyfmt__.Println("accept") + } + goto ret0 + } + + if yyn == 0 { + /* error ... attempt to resume parsing */ + switch Errflag { + case 0: /* brand new error */ + if yyDebug >= 1 { + __yyfmt__.Printf("no action for %s in state %d\n", yySymName(yychar), yystate) + } + msg, ok := yyXErrors[yyXError{yystate, yyxchar}] + if !ok { + msg, ok = yyXErrors[yyXError{yystate, -1}] + } + if !ok && yyshift != 0 { + msg, ok = yyXErrors[yyXError{yyshift, yyxchar}] + } + if !ok { + msg, ok = yyXErrors[yyXError{yyshift, -1}] + } + if !ok || msg == "" { + msg = "syntax error" + } + yylex.Error(msg) + Nerrs++ + fallthrough + + case 1, 2: /* incompletely recovered error ... try again */ + Errflag = 3 + + /* find a state where "error" is a legal shift action */ + for yyp >= 0 { + row := yyParseTab[yyS[yyp].yys] + if yyError < len(row) { + yyn = int(row[yyError]) + yyTabOfs + if yyn > 0 { // hit + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery found error shift in state %d\n", yyS[yyp].yys) + } + yystate = yyn /* simulate a shift of "error" */ + goto yystack + } + } + + /* the current p has no shift on "error", pop stack */ + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) + } + yyp-- + } + /* there is no state on the stack with an error shift ... abort */ + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery failed\n") + } + goto ret1 + + case 3: /* no shift yet; clobber input char */ + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery discards %s\n", yySymName(yychar)) + } + if yychar == yyEOFCode { + goto ret1 + } + + yychar = -1 + goto yynewstate /* try again in the same state */ + } + } + + r := -yyn + x0 := yyReductions[r] + x, n := x0.xsym, x0.components + yypt := yyp + _ = yypt // guard against "declared and not used" + + yyp -= n + if yyp+1 >= len(yyS) { + nyys := make([]yySymType, len(yyS)*2) + copy(nyys, yyS) + yyS = nyys + } + yyVAL = yyS[yyp+1] + + /* consult goto table to find next state */ + exState := yystate + yystate = int(yyParseTab[yyS[yyp].yys][x]) + yyTabOfs + /* reduction by production r */ + if yyDebug >= 2 { + __yyfmt__.Printf("reduce using rule %v (%s), and goto state %d\n", r, yySymNames[x], yystate) + } + + switch r { + case 2: + { + yylex.(*lexer).expr = expr(yyS[yypt-0].item) + } + case 3: + { + yyVAL.item = &alterTableAddStmt{tableName: yyS[yypt-2].item.(string), c: yyS[yypt-0].item.(*col)} + } + case 4: + { + yyVAL.item = &alterTableDropColumnStmt{tableName: yyS[yypt-3].item.(string), colName: yyS[yypt-0].item.(string)} + } + case 5: + { + yyVAL.item = assignment{colName: yyS[yypt-2].item.(string), expr: expr(yyS[yypt-0].item)} + } + case 6: + { + yyVAL.item = append([]assignment{yyS[yypt-2].item.(assignment)}, yyS[yypt-1].item.([]assignment)...) + } + case 7: + { + yyVAL.item = []assignment{} + } + case 8: + { + yyVAL.item = append(yyS[yypt-2].item.([]assignment), yyS[yypt-0].item.(assignment)) + } + case 9: + { + yyVAL.item = beginTransactionStmt{} + } + case 10: + { + yyVAL.item = yyS[yypt-1].item + } + case 11: + { + yyVAL.item = '*' + } + case 12: + { + yyVAL.item = []expression{} + } + case 14: + { + x := &col{name: yyS[yypt-3].item.(string), typ: yyS[yypt-2].item.(int), constraint: yyS[yypt-1].item.(*constraint)} + if yyS[yypt-0].item != nil { + x.dflt = expr(yyS[yypt-0].item) + } + yyVAL.item = x + } + case 16: + { + yyVAL.item = append([]string{yyS[yypt-2].item.(string)}, yyS[yypt-1].item.([]string)...) + } + case 17: + { + yyVAL.item = []string{} + } + case 18: + { + yyVAL.item = append(yyS[yypt-2].item.([]string), yyS[yypt-0].item.(string)) + } + case 19: + { + yyVAL.item = commitStmt{} + } + case 20: + { + yyVAL.item = &constraint{} + } + case 21: + { + yyVAL.item = &constraint{expr(yyS[yypt-0].item)} + } + case 22: + { + yyVAL.item = (*constraint)(nil) + } + case 24: + { + yyVAL.item = &conversion{typ: yyS[yypt-3].item.(int), val: expr(yyS[yypt-1].item)} + } + case 25: + { + indexName, tableName, exprList := yyS[yypt-5].item.(string), yyS[yypt-3].item.(string), yyS[yypt-1].item.([]expression) + simpleIndex := len(exprList) == 1 + var columnName string + if simpleIndex { + expr := exprList[0] + switch x := expr.(type) { + case *ident: + columnName = x.s + case *call: + if x.f == "id" && len(x.arg) == 0 { + columnName = "id()" + break + } + + simpleIndex = false + default: + simpleIndex = false + } + } + + if !simpleIndex { + columnName = "" + } + yyVAL.item = &createIndexStmt{unique: yyS[yypt-8].item.(bool), ifNotExists: yyS[yypt-6].item.(bool), indexName: indexName, tableName: tableName, colName: columnName, exprList: exprList} + + if indexName == tableName || indexName == columnName { + yylex.(*lexer).err("index name collision: %s", indexName) + return 1 + } + + if yylex.(*lexer).root { + break + } + + if isSystemName[indexName] || isSystemName[tableName] { + yylex.(*lexer).err("name is used for system tables: %s", indexName) + return 1 + } + } + case 26: + { + yyVAL.item = false + } + case 27: + { + yyVAL.item = true + } + case 28: + { + yyVAL.item = false + } + case 29: + { + yyVAL.item = true + } + case 30: + { + nm := yyS[yypt-5].item.(string) + yyVAL.item = &createTableStmt{tableName: nm, cols: append([]*col{yyS[yypt-3].item.(*col)}, yyS[yypt-2].item.([]*col)...)} + + if yylex.(*lexer).root { + break + } + + if isSystemName[nm] { + yylex.(*lexer).err("name is used for system tables: %s", nm) + return 1 + } + } + case 31: + { + nm := yyS[yypt-5].item.(string) + yyVAL.item = &createTableStmt{ifNotExists: true, tableName: nm, cols: append([]*col{yyS[yypt-3].item.(*col)}, yyS[yypt-2].item.([]*col)...)} + + if yylex.(*lexer).root { + break + } + + if isSystemName[nm] { + yylex.(*lexer).err("name is used for system tables: %s", nm) + return 1 + } + } + case 32: + { + yyVAL.item = []*col{} + } + case 33: + { + yyVAL.item = append(yyS[yypt-2].item.([]*col), yyS[yypt-0].item.(*col)) + } + case 34: + { + yyVAL.item = yyS[yypt-0].item + } + case 35: + { + yyVAL.item = nil + } + case 37: + { + yyVAL.item = &truncateTableStmt{yyS[yypt-0].item.(string)} + + if yylex.(*lexer).root { + break + } + + if isSystemName[yyS[yypt-0].item.(string)] { + yylex.(*lexer).err("name is used for system tables: %s", yyS[yypt-0].item.(string)) + return 1 + } + } + case 38: + { + yyVAL.item = &deleteStmt{tableName: yyS[yypt-1].item.(string), where: yyS[yypt-0].item.(*whereRset).expr} + + if yylex.(*lexer).root { + break + } + + if isSystemName[yyS[yypt-1].item.(string)] { + yylex.(*lexer).err("name is used for system tables: %s", yyS[yypt-1].item.(string)) + return 1 + } + } + case 39: + { + yyVAL.item = &dropIndexStmt{ifExists: yyS[yypt-1].item.(bool), indexName: yyS[yypt-0].item.(string)} + } + case 40: + { + yyVAL.item = false + } + case 41: + { + yyVAL.item = true + } + case 42: + { + nm := yyS[yypt-0].item.(string) + yyVAL.item = &dropTableStmt{tableName: nm} + + if yylex.(*lexer).root { + break + } + + if isSystemName[nm] { + yylex.(*lexer).err("name is used for system tables: %s", nm) + return 1 + } + } + case 43: + { + nm := yyS[yypt-0].item.(string) + yyVAL.item = &dropTableStmt{ifExists: true, tableName: nm} + + if yylex.(*lexer).root { + break + } + + if isSystemName[nm] { + yylex.(*lexer).err("name is used for system tables: %s", nm) + return 1 + } + } + case 44: + { + yyVAL.item = nil + } + case 45: + { + yyVAL.item = &explainStmt{yyS[yypt-0].item.(stmt)} + } + case 47: + { + var err error + if yyVAL.item, err = newBinaryOperation(oror, yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 50: + { + yyVAL.item = append([]expression{expr(yyS[yypt-2].item)}, yyS[yypt-1].item.([]expression)...) + } + case 51: + { + yyVAL.item = []expression(nil) + } + case 52: + { + yyVAL.item = append(yyS[yypt-2].item.([]expression), expr(yyS[yypt-0].item)) + } + case 54: + { + yyVAL.item = &pIn{expr: yyS[yypt-4].item.(expression), list: yyS[yypt-1].item.([]expression)} + } + case 55: + { + yyVAL.item = &pIn{expr: yyS[yypt-5].item.(expression), not: true, list: yyS[yypt-1].item.([]expression)} + } + case 56: + { + yyVAL.item = &pIn{expr: yyS[yypt-5].item.(expression), sel: yyS[yypt-2].item.(*selectStmt)} + } + case 57: + { + yyVAL.item = &pIn{expr: yyS[yypt-6].item.(expression), not: true, sel: yyS[yypt-2].item.(*selectStmt)} + } + case 58: + { + var err error + if yyVAL.item, err = newBetween(yyS[yypt-4].item, yyS[yypt-2].item, yyS[yypt-0].item, false); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 59: + { + var err error + if yyVAL.item, err = newBetween(yyS[yypt-5].item, yyS[yypt-2].item, yyS[yypt-0].item, true); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 60: + { + yyVAL.item = &isNull{expr: yyS[yypt-2].item.(expression)} + } + case 61: + { + yyVAL.item = &isNull{expr: yyS[yypt-3].item.(expression), not: true} + } + case 63: + { + var err error + if yyVAL.item, err = newBinaryOperation(ge, yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 64: + { + var err error + if yyVAL.item, err = newBinaryOperation('>', yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 65: + { + var err error + if yyVAL.item, err = newBinaryOperation(le, yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 66: + { + var err error + if yyVAL.item, err = newBinaryOperation('<', yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 67: + { + var err error + if yyVAL.item, err = newBinaryOperation(neq, yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 68: + { + var err error + if yyVAL.item, err = newBinaryOperation(eq, yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 69: + { + yyVAL.item = &pLike{expr: yyS[yypt-2].item.(expression), pattern: yyS[yypt-0].item.(expression)} + } + case 70: + { + expr, name := expr(yyS[yypt-1].item), yyS[yypt-0].item.(string) + if name == "" { + s, ok := expr.(*ident) + if ok { + name = s.s + } + } + yyVAL.item = &fld{expr: expr, name: name} + } + case 71: + { + yyVAL.item = "" + } + case 72: + { + yyVAL.item = yyS[yypt-0].item + } + case 73: + { + yyVAL.item = []*fld{yyS[yypt-0].item.(*fld)} + } + case 74: + { + l, f := yyS[yypt-2].item.([]*fld), yyS[yypt-0].item.(*fld) + if f.name != "" { + if f := findFld(l, f.name); f != nil { + yylex.(*lexer).err("duplicate field name %q", f.name) + return 1 + } + } + + yyVAL.item = append(yyS[yypt-2].item.([]*fld), yyS[yypt-0].item.(*fld)) + } + case 75: + { + yyVAL.item = &groupByRset{colNames: yyS[yypt-0].item.([]string)} + } + case 76: + { + yyVAL.item = yyS[yypt-1].item + } + case 77: + { + yyVAL.item = &insertIntoStmt{tableName: yyS[yypt-7].item.(string), colNames: yyS[yypt-6].item.([]string), lists: append([][]expression{yyS[yypt-3].item.([]expression)}, yyS[yypt-1].item.([][]expression)...)} + + if yylex.(*lexer).root { + break + } + + if isSystemName[yyS[yypt-7].item.(string)] { + yylex.(*lexer).err("name is used for system tables: %s", yyS[yypt-7].item.(string)) + return 1 + } + } + case 78: + { + yyVAL.item = &insertIntoStmt{tableName: yyS[yypt-2].item.(string), colNames: yyS[yypt-1].item.([]string), sel: yyS[yypt-0].item.(*selectStmt)} + } + case 79: + { + yyVAL.item = []string{} + } + case 80: + { + yyVAL.item = yyS[yypt-1].item + } + case 81: + { + yyVAL.item = [][]expression{} + } + case 82: + { + yyVAL.item = append(yyS[yypt-4].item.([][]expression), yyS[yypt-1].item.([]expression)) + } + case 90: + { + yyVAL.item = value{yyS[yypt-0].item} + } + case 91: + { + n := yyS[yypt-0].item.(int) + yyVAL.item = parameter{n} + l := yylex.(*lexer) + l.params = mathutil.Max(l.params, n) + if n == 0 { + l.err("parameter number must be non zero") + return 1 + } + } + case 92: + { + yyVAL.item = &ident{yyS[yypt-0].item.(string)} + } + case 93: + { + yyVAL.item = &pexpr{expr: expr(yyS[yypt-1].item)} + } + case 94: + { + yyVAL.item = &orderByRset{by: yyS[yypt-1].item.([]expression), asc: yyS[yypt-0].item.(bool)} + } + case 95: + { + yyVAL.item = true // ASC by default + } + case 96: + { + yyVAL.item = true + } + case 97: + { + yyVAL.item = false + } + case 100: + { + var err error + if yyVAL.item, err = newIndex(yyS[yypt-1].item.(expression), expr(yyS[yypt-0].item)); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 101: + { + var err error + s := yyS[yypt-0].item.([2]*expression) + if yyVAL.item, err = newSlice(yyS[yypt-1].item.(expression), s[0], s[1]); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 102: + { + x := yylex.(*lexer) + f, ok := yyS[yypt-1].item.(*ident) + if !ok { + x.err("expected identifier or qualified identifier") + return 1 + } + + if r, ok := yyS[yypt-0].item.(rune); ok { + if f.isQualified() || f.s != "count" || r != '*' { + x.err(fmt.Sprintf("invalid expression %s(%c)", f, r)) + return 1 + } + + yyS[yypt-0].item = []expression(nil) + } + + var err error + var agg bool + if yyVAL.item, agg, err = newCall(f.s, yyS[yypt-0].item.([]expression)); err != nil { + x.err("%v", err) + return 1 + } + if n := len(x.agg); n > 0 { + x.agg[n-1] = x.agg[n-1] || agg + } + } + case 104: + { + var err error + if yyVAL.item, err = newBinaryOperation('^', yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 105: + { + var err error + if yyVAL.item, err = newBinaryOperation('|', yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 106: + { + var err error + if yyVAL.item, err = newBinaryOperation('-', yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 107: + { + var err error + yyVAL.item, err = newBinaryOperation('+', yyS[yypt-2].item, yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 109: + { + var err error + yyVAL.item, err = newBinaryOperation(andnot, yyS[yypt-2].item, yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 110: + { + var err error + yyVAL.item, err = newBinaryOperation('&', yyS[yypt-2].item, yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 111: + { + var err error + yyVAL.item, err = newBinaryOperation(lsh, yyS[yypt-2].item, yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 112: + { + var err error + yyVAL.item, err = newBinaryOperation(rsh, yyS[yypt-2].item, yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 113: + { + var err error + yyVAL.item, err = newBinaryOperation('%', yyS[yypt-2].item, yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 114: + { + var err error + yyVAL.item, err = newBinaryOperation('/', yyS[yypt-2].item, yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 115: + { + var err error + yyVAL.item, err = newBinaryOperation('*', yyS[yypt-2].item, yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 117: + { + yyVAL.item = fmt.Sprintf("%s.%s", yyS[yypt-2].item.(string), yyS[yypt-0].item.(string)) + } + case 118: + { + yyVAL.item = []interface{}{yyS[yypt-1].item, yyS[yypt-0].item} + } + case 120: + { + yyVAL.item = yyS[yypt-2].item + } + case 123: + { + yyVAL.item = "" + } + case 124: + { + yyVAL.item = yyS[yypt-0].item + } + case 125: + { + yyVAL.list = []interface{}{yyS[yypt-0].item} + } + case 126: + { + yyVAL.list = append(yyS[yypt-2].list, yyS[yypt-0].item) + } + case 127: + { + yyVAL.item = rollbackStmt{} + } + case 128: + { + yyVAL.item = leftJoin + } + case 129: + { + yyVAL.item = rightJoin + } + case 130: + { + yyVAL.item = fullJoin + } + case 131: + { + yyVAL.item = nil + } + case 133: + { + yyVAL.item = []interface{}{yyS[yypt-5].item, yyS[yypt-2].item, yyS[yypt-0].item} + } + case 134: + { + yyVAL.item = nil + } + case 136: + { + x := yylex.(*lexer) + n := len(x.agg) + join := &joinRset{sources: yyS[yypt-7].list} + if o := yyS[yypt-5].item; o != nil { + o := o.([]interface{}) + join.typ = o[0].(int) + join.sources = append(join.sources, o[1].([]interface{})) + join.on = o[2].(expression) + } + yyVAL.item = &selectStmt{ + distinct: yyS[yypt-10].item.(bool), + flds: yyS[yypt-9].item.([]*fld), + from: join, + hasAggregates: x.agg[n-1], + where: yyS[yypt-4].item.(*whereRset), + group: yyS[yypt-3].item.(*groupByRset), + order: yyS[yypt-2].item.(*orderByRset), + limit: yyS[yypt-1].item.(*limitRset), + offset: yyS[yypt-0].item.(*offsetRset), + } + x.agg = x.agg[:n-1] + } + case 137: + { + yyVAL.item = (*limitRset)(nil) + } + case 138: + { + yyVAL.item = &limitRset{expr: expr(yyS[yypt-0].item)} + } + case 139: + { + yyVAL.item = (*offsetRset)(nil) + } + case 140: + { + yyVAL.item = &offsetRset{expr: expr(yyS[yypt-0].item)} + } + case 141: + { + yyVAL.item = false + } + case 142: + { + yyVAL.item = true + } + case 143: + { + yyVAL.item = []*fld{} + } + case 144: + { + yyVAL.item = yyS[yypt-0].item + } + case 145: + { + yyVAL.item = yyS[yypt-1].item + } + case 146: + { + yyVAL.item = (*whereRset)(nil) + } + case 148: + { + yyVAL.item = (*groupByRset)(nil) + } + case 150: + { + yyVAL.item = (*orderByRset)(nil) + } + case 152: + { + yyVAL.item = [2]*expression{nil, nil} + } + case 153: + { + hi := expr(yyS[yypt-1].item) + yyVAL.item = [2]*expression{nil, &hi} + } + case 154: + { + lo := expr(yyS[yypt-2].item) + yyVAL.item = [2]*expression{&lo, nil} + } + case 155: + { + lo := expr(yyS[yypt-3].item) + hi := expr(yyS[yypt-1].item) + yyVAL.item = [2]*expression{&lo, &hi} + } + case 171: + { + if yyS[yypt-0].item != nil { + yylex.(*lexer).list = []stmt{yyS[yypt-0].item.(stmt)} + } + } + case 172: + { + if yyS[yypt-0].item != nil { + yylex.(*lexer).list = append(yylex.(*lexer).list, yyS[yypt-0].item.(stmt)) + } + } + case 175: + { + var err error + if yyVAL.item, err = newBinaryOperation(andand, yyS[yypt-2].item, yyS[yypt-0].item); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 178: + { + yyVAL.item = &truncateTableStmt{tableName: yyS[yypt-0].item.(string)} + } + case 203: + { + var expr expression + if w := yyS[yypt-0].item; w != nil { + expr = w.(*whereRset).expr + } + yyVAL.item = &updateStmt{tableName: yyS[yypt-3].item.(string), list: yyS[yypt-1].item.([]assignment), where: expr} + + if yylex.(*lexer).root { + break + } + + if isSystemName[yyS[yypt-3].item.(string)] { + yylex.(*lexer).err("name is used for system tables: %s", yyS[yypt-3].item.(string)) + return 1 + } + } + case 204: + { + yyVAL.item = nil + } + case 207: + { + var err error + yyVAL.item, err = newUnaryOperation('^', yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 208: + { + var err error + yyVAL.item, err = newUnaryOperation('!', yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 209: + { + var err error + yyVAL.item, err = newUnaryOperation('-', yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 210: + { + var err error + yyVAL.item, err = newUnaryOperation('+', yyS[yypt-0].item) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + case 211: + { + yyVAL.item = &whereRset{expr: expr(yyS[yypt-0].item)} + } + + } + + if yyEx != nil && yyEx.Reduced(r, exState, &yyVAL) { + return -1 + } + goto yystack /* stack new state and value */ +} + +func expr(v interface{}) expression { + e := v.(expression) + for { + x, ok := e.(*pexpr) + if !ok { + return e + } + e = x.expr + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/parser.y b/Godeps/_workspace/src/github.com/cznic/ql/parser.y new file mode 100644 index 000000000..979c3289a --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/parser.y @@ -0,0 +1,1337 @@ +%{ +// Copyright (c) 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Inital yacc source generated by ebnf2y[1] +// at 2013-10-04 23:10:47.861401015 +0200 CEST +// +// $ ebnf2y -o ql.y -oe ql.ebnf -start StatementList -pkg ql -p _ +// +// [1]: http://github.com/cznic/ebnf2y + +package ql + +import ( + "fmt" + + "github.com/cznic/mathutil" +) + +%} + +%union { + line int + col int + item interface{} + list []interface{} +} + +%token + + /*yy:token "1.%d" */ floatLit "floating-point literal" + /*yy:token "%c" */ identifier "identifier" + /*yy:token "%di" */ imaginaryLit "imaginary literal" + /*yy:token "%d" */ intLit "integer literal" + /*yy:token "$%d" */ qlParam "QL parameter" + /*yy:token "\"%c\"" */ stringLit "string literal" + + add "ADD" + alter "ALTER" + and "AND" + andand "&&" + andnot "&^" + as "AS" + asc "ASC" + begin "BEGIN" + between "BETWEEN" + bigIntType "bigint" + bigRatType "bigrat" + blobType "blob" + boolType "bool" + by "BY" + byteType "byte" + column "COLUMN" + commit "COMMIT" + complex128Type "complex128" + complex64Type "complex64" + create "CREATE" + defaultKwd "DEFAULT" + deleteKwd "DELETE" + desc "DESC" + distinct "DISTINCT" + drop "DROP" + durationType "duration" + eq "==" + exists "EXISTS" + explain "EXPLAIN" + falseKwd "false" + floatType "float" + float32Type "float32" + float64Type "float64" + from "FROM" + full "FULL" + ge ">=" + group "GROUP" + ifKwd "IF" + in "IN" + index "INDEX" + insert "INSERT" + intType "int" + int16Type "int16" + int32Type "int32" + int64Type "int64" + int8Type "int8" + into "INTO" + is "IS" + join "JOIN" + le "<=" + left "LEFT" + like "LIKE" + limit "LIMIT" + lsh "<<" + neq "!=" + not "NOT" + null "NULL" + offset "OFFSET" + on "ON" + or "OR" + order "ORDER" + oror "||" + outer "OUTER" + right "RIGHT" + rollback "ROLLBACK" + rsh ">>" + runeType "rune" + selectKwd "SELECT" + set "SET" + stringType "string" + tableKwd "TABLE" + timeType "time" + transaction "TRANSACTION" + trueKwd "true" + truncate "TRUNCATE" + uintType "uint" + uint16Type "uint16" + uint32Type "uint32" + uint64Type "uint64" + uint8Type "uint8", + unique "UNIQUE" + update "UPDATE" + values "VALUES" + where "WHERE" + + parseExpression "parse expression prefix" + +%type + AlterTableStmt "ALTER TABLE statement" + Assignment "assignment" + AssignmentList "assignment list" + AssignmentList1 "assignment list optional trailing comma" + BeginTransactionStmt "BEGIN TRANSACTION statement" + Call "function call" + Call1 "function call optional argument list" + ColumnDef "table column definition" + ColumnName "column name" + ColumnNameList "column name list" + ColumnNameList1 "column name list with optional trailing comma" + CommaOpt "optional comma" + CommitStmt "COMMIT statement" + Constraint "column value constraint" + ConstraintOpt "optional column value constraint" + Conversion "conversion" + CreateIndexStmt "CREATE INDEX statement" + CreateIndexIfNotExists "CREATE INDEX statement optional IF NOT EXISTS cluse" + CreateIndexStmtUnique "CREATE INDEX optional UNIQUE clause" + CreateTableStmt "CREATE TABLE statement" + CreateTableStmt1 "CREATE TABLE statement colum definition list" + Default "DEFAULT clause" + DefaultOpt "optional DEFAULT clause" + DeleteFromStmt "DELETE FROM statement" + DropIndexStmt "DROP INDEX statement" + DropIndexIfExists "DROP INDEX statement optional IF EXISTS clause" + DropTableStmt "DROP TABLE statement" + EmptyStmt "empty statement" + ExplainStmt "EXPLAIN statement" + Expression "expression" + ExpressionList "expression list" + ExpressionList1 "expression list expression" + Factor "expression factor" + Factor1 "binary expression factor" + Field "field expression" + Field1 "field expression optional AS clause" + FieldList "field expression list" + GroupByClause "GROUP BY clause" + Index "string index" + InsertIntoStmt "INSERT INTO statement" + InsertIntoStmt1 "INSERT INTO statement optional column list clause" + InsertIntoStmt2 "INSERT INTO statement optional values list" + JoinClause "SELECT statement JOIN clause" + JoinClauseOpt "SELECT statement optional JOIN clause" + JoinType "join type" + Literal "literal value" + logAnd "logical and operator" + logOr "logical or operator" + Operand "operand" + OrderBy "ORDER BY clause" + OrderBy1 "ORDER BY clause optional collation specification" + OuterOpt "optional OUTER clause" + QualifiedIdent "qualified identifier" + PrimaryExpression "primary expression" + PrimaryFactor "primary expression factor" + PrimaryTerm "primary expression term" + RecordSet "record set" + RecordSet1 "record set name or parenthesized SELECTECT statement" + RecordSet2 "record set optional AS clause" + RollbackStmt "ROLLBACK statement" + SelectStmt "SELECT statement" + SelectStmtDistinct "SELECT statement optional DISTINCT clause" + SelectStmtFieldList "SELECT statement field list" + SelectStmtLimit "SELECT statement optional LIMIT clause" + SelectStmtWhere "SELECT statement optional WHERE clause" + SelectStmtGroup "SELECT statement optional GROUP BY clause" + SelectStmtOffset "SELECT statement optional OFFSET clause" + SelectStmtOrder "SELECT statement optional ORDER BY clause" + Slice "string slice" + Statement "statement" + StatementList "statement list" + TableName "table name" + Term "expression term" + TruncateTableStmt "TRANSACTION TABLE statement" + Type "type" + UnaryExpr "unary expression" + UpdateStmt "UPDATE statement" + UpdateStmt1 "UPDATE statement optional WHERE clause" + WhereClause "WHERE clause" + +%type RecordSetList + +%start Start + +%% + +Start: + StatementList +| parseExpression Expression + { + yylex.(*lexer).expr = expr($2) + } + +AlterTableStmt: + "ALTER" "TABLE" TableName "ADD" ColumnDef + { + $$ = &alterTableAddStmt{tableName: $3.(string), c: $5.(*col)} + } +| "ALTER" "TABLE" TableName "DROP" "COLUMN" ColumnName + { + $$ = &alterTableDropColumnStmt{tableName: $3.(string), colName: $6.(string)} + } + +Assignment: + ColumnName '=' Expression + { + $$ = assignment{colName: $1.(string), expr: expr($3)} + } + +AssignmentList: + Assignment AssignmentList1 CommaOpt + { + $$ = append([]assignment{$1.(assignment)}, $2.([]assignment)...) + } + +AssignmentList1: + /* EMPTY */ + { + $$ = []assignment{} + } +| AssignmentList1 ',' Assignment + { + $$ = append($1.([]assignment), $3.(assignment)) + } + +BeginTransactionStmt: + "BEGIN" "TRANSACTION" + { + $$ = beginTransactionStmt{} + } + +Call: + '(' Call1 ')' + { + $$ = $2 + } +| '(' '*' ')' + { + $$ = '*' + } + +Call1: + /* EMPTY */ + { + $$ = []expression{} + } +| ExpressionList + +ColumnDef: + ColumnName Type ConstraintOpt DefaultOpt + { + x := &col{name: $1.(string), typ: $2.(int), constraint: $3.(*constraint)} + if $4 != nil { + x.dflt = expr($4) + } + $$ = x + } + +ColumnName: + identifier + +ColumnNameList: + ColumnName ColumnNameList1 CommaOpt + { + $$ = append([]string{$1.(string)}, $2.([]string)...) + } + +ColumnNameList1: + /* EMPTY */ + { + $$ = []string{} + } +| ColumnNameList1 ',' ColumnName + { + $$ = append($1.([]string), $3.(string)) + } + +CommitStmt: + "COMMIT" + { + $$ = commitStmt{} + } + +Constraint: + "NOT" "NULL" + { + $$ = &constraint{} + } +| Expression + { + $$ = &constraint{expr($1)} + } + +ConstraintOpt: + { + $$ = (*constraint)(nil) + } +| Constraint + +Conversion: + Type '(' Expression ')' + { + $$ = &conversion{typ: $1.(int), val: expr($3)} + } + +CreateIndexStmt: + "CREATE" CreateIndexStmtUnique "INDEX" CreateIndexIfNotExists identifier "ON" identifier '(' ExpressionList ')' + { + indexName, tableName, exprList := $5.(string), $7.(string), $9.([]expression) + simpleIndex := len(exprList) == 1 + var columnName string + if simpleIndex { + expr := exprList[0] + switch x := expr.(type) { + case *ident: + columnName = x.s + case *call: + if x.f == "id" && len(x.arg) == 0 { + columnName = "id()" + break + } + + simpleIndex = false + default: + simpleIndex = false + } + } + + if !simpleIndex { + columnName = "" + } + $$ = &createIndexStmt{unique: $2.(bool), ifNotExists: $4.(bool), indexName: indexName, tableName: tableName, colName: columnName, exprList: exprList} + + if indexName == tableName || indexName == columnName { + yylex.(*lexer).err("index name collision: %s", indexName) + return 1 + } + + if yylex.(*lexer).root { + break + } + + if isSystemName[indexName] || isSystemName[tableName] { + yylex.(*lexer).err("name is used for system tables: %s", indexName) + return 1 + } + } + +CreateIndexIfNotExists: + { + $$ = false + } +| "IF" "NOT" "EXISTS" + { + $$ = true + } + +CreateIndexStmtUnique: + { + $$ = false + } +| "UNIQUE" + { + $$ = true + } + +CreateTableStmt: + "CREATE" "TABLE" TableName '(' ColumnDef CreateTableStmt1 CommaOpt ')' + { + nm := $3.(string) + $$ = &createTableStmt{tableName: nm, cols: append([]*col{$5.(*col)}, $6.([]*col)...)} + + if yylex.(*lexer).root { + break + } + + if isSystemName[nm] { + yylex.(*lexer).err("name is used for system tables: %s", nm) + return 1 + } + } +| "CREATE" "TABLE" "IF" "NOT" "EXISTS" TableName '(' ColumnDef CreateTableStmt1 CommaOpt ')' + { + nm := $6.(string) + $$ = &createTableStmt{ifNotExists: true, tableName: nm, cols: append([]*col{$8.(*col)}, $9.([]*col)...)} + + if yylex.(*lexer).root { + break + } + + if isSystemName[nm] { + yylex.(*lexer).err("name is used for system tables: %s", nm) + return 1 + } + } + +CreateTableStmt1: + /* EMPTY */ + { + $$ = []*col{} + } +| CreateTableStmt1 ',' ColumnDef + { + $$ = append($1.([]*col), $3.(*col)) + } + +Default: + "DEFAULT" Expression + { + $$ = $2 + } + +DefaultOpt: + { + $$ = nil + } +| Default + +DeleteFromStmt: + "DELETE" "FROM" TableName + { + $$ = &truncateTableStmt{$3.(string)} + + if yylex.(*lexer).root { + break + } + + if isSystemName[$3.(string)] { + yylex.(*lexer).err("name is used for system tables: %s", $3.(string)) + return 1 + } + } +| "DELETE" "FROM" TableName WhereClause + { + $$ = &deleteStmt{tableName: $3.(string), where: $4.(*whereRset).expr} + + if yylex.(*lexer).root { + break + } + + if isSystemName[$3.(string)] { + yylex.(*lexer).err("name is used for system tables: %s", $3.(string)) + return 1 + } + } + +DropIndexStmt: + "DROP" "INDEX" DropIndexIfExists identifier + { + $$ = &dropIndexStmt{ifExists: $3.(bool), indexName: $4.(string)} + } + +DropIndexIfExists: + { + $$ = false + } +| "IF" "EXISTS" + { + $$ = true + } + +DropTableStmt: + "DROP" "TABLE" TableName + { + nm := $3.(string) + $$ = &dropTableStmt{tableName: nm} + + if yylex.(*lexer).root { + break + } + + if isSystemName[nm] { + yylex.(*lexer).err("name is used for system tables: %s", nm) + return 1 + } + } +| "DROP" "TABLE" "IF" "EXISTS" TableName + { + nm := $5.(string) + $$ = &dropTableStmt{ifExists: true, tableName: nm} + + if yylex.(*lexer).root { + break + } + + if isSystemName[nm] { + yylex.(*lexer).err("name is used for system tables: %s", nm) + return 1 + } + } + +EmptyStmt: + /* EMPTY */ + { + $$ = nil + } + +ExplainStmt: + "EXPLAIN" Statement + { + $$ = &explainStmt{$2.(stmt)} + } + +Expression: + Term +| Expression logOr Term + { + var err error + if $$, err = newBinaryOperation(oror, $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + +logOr: + "||" + { + } +| "OR" + { + } + +ExpressionList: + Expression ExpressionList1 CommaOpt + { + $$ = append([]expression{expr($1)}, $2.([]expression)...) + } + +ExpressionList1: + /* EMPTY */ + { + $$ = []expression(nil) + } +| ExpressionList1 ',' Expression + { + $$ = append($1.([]expression), expr($3)) + } + +Factor: + Factor1 +| Factor1 "IN" '(' ExpressionList ')' + { + $$ = &pIn{expr: $1.(expression), list: $4.([]expression)} + } +| Factor1 "NOT" "IN" '(' ExpressionList ')' + { + $$ = &pIn{expr: $1.(expression), not: true, list: $5.([]expression)} + } +| Factor1 "IN" '(' SelectStmt semiOpt ')' + { + $$ = &pIn{expr: $1.(expression), sel: $4.(*selectStmt)} + } +| Factor1 "NOT" "IN" '(' SelectStmt semiOpt ')' + { + $$ = &pIn{expr: $1.(expression), not: true, sel: $5.(*selectStmt)} + } +| Factor1 "BETWEEN" PrimaryFactor "AND" PrimaryFactor + { + var err error + if $$, err = newBetween($1, $3, $5, false); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| Factor1 "NOT" "BETWEEN" PrimaryFactor "AND" PrimaryFactor + { + var err error + if $$, err = newBetween($1, $4, $6, true); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| Factor1 "IS" "NULL" + { + $$ = &isNull{expr: $1.(expression)} + } +| Factor1 "IS" "NOT" "NULL" + { + $$ = &isNull{expr: $1.(expression), not: true} + } + +Factor1: + PrimaryFactor +| Factor1 ">=" PrimaryFactor + { + var err error + if $$, err = newBinaryOperation(ge, $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| Factor1 '>' PrimaryFactor + { + var err error + if $$, err = newBinaryOperation('>', $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| Factor1 "<=" PrimaryFactor + { + var err error + if $$, err = newBinaryOperation(le, $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| Factor1 '<' PrimaryFactor + { + var err error + if $$, err = newBinaryOperation('<', $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| Factor1 "!=" PrimaryFactor + { + var err error + if $$, err = newBinaryOperation(neq, $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| Factor1 "==" PrimaryFactor + { + var err error + if $$, err = newBinaryOperation(eq, $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| Factor1 "LIKE" PrimaryFactor + { + $$ = &pLike{expr: $1.(expression), pattern: $3.(expression)} + } + +Field: + Expression Field1 + { + expr, name := expr($1), $2.(string) + if name == "" { + s, ok := expr.(*ident) + if ok { + name = s.s + } + } + $$ = &fld{expr: expr, name: name} + } + +Field1: + /* EMPTY */ + { + $$ = "" + } +| "AS" identifier + { + $$ = $2 + } + +FieldList: + Field + { + $$ = []*fld{$1.(*fld)} + } +| FieldList ',' Field + { + l, f := $1.([]*fld), $3.(*fld) + if f.name != "" { + if f := findFld(l, f.name); f != nil { + yylex.(*lexer).err("duplicate field name %q", f.name) + return 1 + } + } + + $$ = append($1.([]*fld), $3.(*fld)) + } + +GroupByClause: + "GROUP" "BY" ColumnNameList + { + $$ = &groupByRset{colNames: $3.([]string)} + } + +Index: + '[' Expression ']' + { + $$ = $2 + } + +InsertIntoStmt: + "INSERT" "INTO" TableName InsertIntoStmt1 "VALUES" '(' ExpressionList ')' InsertIntoStmt2 CommaOpt + { + $$ = &insertIntoStmt{tableName: $3.(string), colNames: $4.([]string), lists: append([][]expression{$7.([]expression)}, $9.([][]expression)...)} + + if yylex.(*lexer).root { + break + } + + if isSystemName[$3.(string)] { + yylex.(*lexer).err("name is used for system tables: %s", $3.(string)) + return 1 + } + } +| "INSERT" "INTO" TableName InsertIntoStmt1 SelectStmt + { + $$ = &insertIntoStmt{tableName: $3.(string), colNames: $4.([]string), sel: $5.(*selectStmt)} + } + +InsertIntoStmt1: + /* EMPTY */ + { + $$ = []string{} + } +| '(' ColumnNameList ')' + { + $$ = $2 + } + +InsertIntoStmt2: + /* EMPTY */ + { + $$ = [][]expression{} + } +| InsertIntoStmt2 ',' '(' ExpressionList ')' + { + $$ = append($1.([][]expression), $4.([]expression)) + } + +Literal: + "false" +| "NULL" +| "true" +| floatLit +| imaginaryLit +| intLit +| stringLit + +Operand: + Literal + { + $$ = value{$1} + } +| qlParam + { + n := $1.(int) + $$ = parameter{n} + l := yylex.(*lexer) + l.params = mathutil.Max(l.params, n) + if n == 0 { + l.err("parameter number must be non zero") + return 1 + } + } +| QualifiedIdent + { + $$ = &ident{$1.(string)} + } +| '(' Expression ')' + { + $$ = &pexpr{expr: expr($2)} + } + +OrderBy: + "ORDER" "BY" ExpressionList OrderBy1 + { + $$ = &orderByRset{by: $3.([]expression), asc: $4.(bool)} + } + +OrderBy1: + /* EMPTY */ + { + $$ = true // ASC by default + } +| "ASC" + { + $$ = true + } +| "DESC" + { + $$ = false + } + +PrimaryExpression: + Operand +| Conversion +| PrimaryExpression Index + { + var err error + if $$, err = newIndex($1.(expression), expr($2)); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryExpression Slice + { + var err error + s := $2.([2]*expression) + if $$, err = newSlice($1.(expression), s[0], s[1]); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryExpression Call + { + x := yylex.(*lexer) + f, ok := $1.(*ident) + if !ok { + x.err("expected identifier or qualified identifier") + return 1 + } + + if r, ok := $2.(rune); ok { + if f.isQualified() || f.s != "count" || r != '*' { + x.err(fmt.Sprintf("invalid expression %s(%c)", f, r)) + return 1 + } + + $2 = []expression(nil) + } + + var err error + var agg bool + if $$, agg, err = newCall(f.s, $2.([]expression)); err != nil { + x.err("%v", err) + return 1 + } + if n := len(x.agg); n > 0 { + x.agg[n-1] = x.agg[n-1] || agg + } + } + +PrimaryFactor: + PrimaryTerm +| PrimaryFactor '^' PrimaryTerm + { + var err error + if $$, err = newBinaryOperation('^', $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryFactor '|' PrimaryTerm + { + var err error + if $$, err = newBinaryOperation('|', $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryFactor '-' PrimaryTerm + { + var err error + if $$, err = newBinaryOperation('-', $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryFactor '+' PrimaryTerm + { + var err error + $$, err = newBinaryOperation('+', $1, $3) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + +PrimaryTerm: + UnaryExpr +| PrimaryTerm "&^" UnaryExpr + { + var err error + $$, err = newBinaryOperation(andnot, $1, $3) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryTerm '&' UnaryExpr + { + var err error + $$, err = newBinaryOperation('&', $1, $3) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryTerm "<<" UnaryExpr + { + var err error + $$, err = newBinaryOperation(lsh, $1, $3) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryTerm ">>" UnaryExpr + { + var err error + $$, err = newBinaryOperation(rsh, $1, $3) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryTerm '%' UnaryExpr + { + var err error + $$, err = newBinaryOperation('%', $1, $3) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryTerm '/' UnaryExpr + { + var err error + $$, err = newBinaryOperation('/', $1, $3) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| PrimaryTerm '*' UnaryExpr + { + var err error + $$, err = newBinaryOperation('*', $1, $3) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + +QualifiedIdent: + identifier +| identifier '.' identifier + { + $$ = fmt.Sprintf("%s.%s", $1.(string), $3.(string)) + } + +RecordSet: + RecordSet1 RecordSet2 + { + $$ = []interface{}{$1, $2} + } + +RecordSet1: + identifier +| '(' SelectStmt semiOpt ')' + { + $$ = $2 + } + +semiOpt: + /* EMPTY */ +| ';' + +RecordSet2: + /* EMPTY */ + { + $$ = "" + } +| "AS" identifier + { + $$ = $2 + } + +RecordSetList: + RecordSet + { + $$ = []interface{}{$1} + } +| RecordSetList ',' RecordSet + { + $$ = append($1, $3) + } + +RollbackStmt: + "ROLLBACK" + { + $$ = rollbackStmt{} + } + +JoinType: + "LEFT" + { + $$ = leftJoin + } +| "RIGHT" + { + $$ = rightJoin + } +| "FULL" + { + $$ = fullJoin + } + +OuterOpt: + { + $$ = nil + } +| "OUTER" + +JoinClause: + JoinType OuterOpt "JOIN" RecordSet "ON" Expression + { + $$ = []interface{}{$1, $4, $6} + } + +JoinClauseOpt: + { + $$ = nil + } +| JoinClause + +SelectStmt: + "SELECT" SelectStmtDistinct SelectStmtFieldList "FROM" RecordSetList + CommaOpt JoinClauseOpt SelectStmtWhere SelectStmtGroup SelectStmtOrder + SelectStmtLimit SelectStmtOffset + { + x := yylex.(*lexer) + n := len(x.agg) + join := &joinRset{sources: $5} + if o := $7; o != nil { + o := o.([]interface{}) + join.typ = o[0].(int) + join.sources = append(join.sources, o[1].([]interface{})) + join.on = o[2].(expression) + } + $$ = &selectStmt{ + distinct: $2.(bool), + flds: $3.([]*fld), + from: join, + hasAggregates: x.agg[n-1], + where: $8.(*whereRset), + group: $9.(*groupByRset), + order: $10.(*orderByRset), + limit: $11.(*limitRset), + offset: $12.(*offsetRset), + } + x.agg = x.agg[:n-1] + } + +SelectStmtLimit: + { + $$ = (*limitRset)(nil) + } +| "LIMIT" Expression + { + $$ = &limitRset{expr: expr($2)} + } + +SelectStmtOffset: + { + $$ = (*offsetRset)(nil) + } +| "OFFSET" Expression + { + $$ = &offsetRset{expr: expr($2)} + } + +SelectStmtDistinct: + /* EMPTY */ + { + $$ = false + } +| "DISTINCT" + { + $$ = true + } + +SelectStmtFieldList: + '*' + { + $$ = []*fld{} + } +| FieldList + { + $$ = $1 + } +| FieldList ',' + { + $$ = $1 + } + +SelectStmtWhere: + /* EMPTY */ + { + $$ = (*whereRset)(nil) + } +| WhereClause + +SelectStmtGroup: + /* EMPTY */ + { + $$ = (*groupByRset)(nil) + } +| GroupByClause + +SelectStmtOrder: + /* EMPTY */ + { + $$ = (*orderByRset)(nil) + } +| OrderBy + +Slice: + '[' ':' ']' + { + $$ = [2]*expression{nil, nil} + } +| '[' ':' Expression ']' + { + hi := expr($3) + $$ = [2]*expression{nil, &hi} + } +| '[' Expression ':' ']' + { + lo := expr($2) + $$ = [2]*expression{&lo, nil} + } +| '[' Expression ':' Expression ']' + { + lo := expr($2) + hi := expr($4) + $$ = [2]*expression{&lo, &hi} + } + +Statement: + EmptyStmt +| AlterTableStmt +| BeginTransactionStmt +| CommitStmt +| CreateIndexStmt +| CreateTableStmt +| DeleteFromStmt +| DropIndexStmt +| DropTableStmt +| ExplainStmt +| InsertIntoStmt +| RollbackStmt +| SelectStmt +| TruncateTableStmt +| UpdateStmt + +StatementList: + Statement + { + if $1 != nil { + yylex.(*lexer).list = []stmt{$1.(stmt)} + } + } +| StatementList ';' Statement + { + if $3 != nil { + yylex.(*lexer).list = append(yylex.(*lexer).list, $3.(stmt)) + } + } + +TableName: + identifier + +Term: + Factor +| Term logAnd Factor + { + var err error + if $$, err = newBinaryOperation(andand, $1, $3); err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + +logAnd: + "&&" + { + } +| "AND" + { + } + +TruncateTableStmt: + "TRUNCATE" "TABLE" TableName + { + $$ = &truncateTableStmt{tableName: $3.(string)} + } + +Type: + "bigint" +| "bigrat" +| "blob" +| "bool" +| "byte" +| "complex128" +| "complex64" +| "duration" +| "float" +| "float32" +| "float64" +| "int" +| "int16" +| "int32" +| "int64" +| "int8" +| "rune" +| "string" +| "time" +| "uint" +| "uint16" +| "uint32" +| "uint64" +| "uint8" + +UpdateStmt: + "UPDATE" TableName SetOpt AssignmentList UpdateStmt1 + { + var expr expression + if w := $5; w != nil { + expr = w.(*whereRset).expr + } + $$ = &updateStmt{tableName: $2.(string), list: $4.([]assignment), where: expr} + + if yylex.(*lexer).root { + break + } + + if isSystemName[$2.(string)] { + yylex.(*lexer).err("name is used for system tables: %s", $2.(string)) + return 1 + } + } + +UpdateStmt1: + /* EMPTY */ + { + $$ = nil + } +| WhereClause + +UnaryExpr: + PrimaryExpression +| '^' PrimaryExpression + { + var err error + $$, err = newUnaryOperation('^', $2) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| '!' PrimaryExpression + { + var err error + $$, err = newUnaryOperation('!', $2) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| '-' PrimaryExpression + { + var err error + $$, err = newUnaryOperation('-', $2) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } +| '+' PrimaryExpression + { + var err error + $$, err = newUnaryOperation('+', $2) + if err != nil { + yylex.(*lexer).err("%v", err) + return 1 + } + } + +WhereClause: + "WHERE" Expression + { + $$ = &whereRset{expr: expr($2)} + } + + +SetOpt: + { + } +| "SET" + { + } + +CommaOpt: + { + } +| ',' + { + } + +%% + +func expr(v interface{}) expression { + e := v.(expression) + for { + x, ok := e.(*pexpr) + if !ok { + return e + } + e = x.expr + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/parser_test.go b/Godeps/_workspace/src/github.com/cznic/ql/parser_test.go new file mode 100644 index 000000000..d603a2839 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/parser_test.go @@ -0,0 +1,193 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "testing" +) + +func TestParser0(t *testing.T) { + table := []struct { + src string + ok bool + }{ + {"", true}, + {";", true}, + {"CREATE", false}, + {"CREATE TABLE", false}, + {"CREATE TABLE foo (", false}, + // 5 + {"CREATE TABLE foo ()", false}, + {"CREATE TABLE foo ();", false}, + {"CREATE TABLE foo (a byte)", true}, + {"CREATE TABLE foo (a uint8);", true}, + {"CREATE TABLE foo (a uint16, b uint32)", true}, + // 10 + {"CREATE TABLE foo (a uint64, b bool);", true}, + {"CREATE TABLE foo (a int8, b int16) CREATE TABLE bar (x int32, y int64)", false}, + {"CREATE TABLE foo (a int, b float32); CREATE TABLE bar (x float64, y float)", true}, + {"INSERT INTO foo VALUES (1234)", true}, + {"INSERT INTO foo VALUES (1234, 5678)", true}, + // 15 + {"INSERT INTO foo VALUES (1 || 2)", false}, + {"INSERT INTO foo VALUES (1 | 2)", true}, + {"INSERT INTO foo VALUES (false || true)", true}, + {"INSERT INTO foo VALUES (id())", true}, + {"INSERT INTO foo VALUES (bar(5678))", false}, + // 20 + {"INSERT INTO foo VALUES ()", false}, + {"CREATE TABLE foo (a.b, b);", false}, + {"CREATE TABLE foo (a, b.c);", false}, + {"SELECT * FROM t", true}, + {"SELECT * FROM t AS u", true}, + // 25 + {"SELECT * FROM t, v", true}, + {"SELECT * FROM t AS u, v", true}, + {"SELECT * FROM t, v AS w", true}, + {"SELECT * FROM t AS u, v AS w", true}, + {"SELECT * FROM foo, bar, foo", true}, + // 30 + {"CREATE TABLE foo (a bytes)", false}, + {"SELECT DISTINCTS * FROM t", false}, + {"SELECT DISTINCT * FROM t", true}, + {"INSERT INTO foo (a) VALUES (42)", true}, + {"INSERT INTO foo (a,) VALUES (42,)", true}, + // 35 + {"INSERT INTO foo (a,b) VALUES (42,314)", true}, + {"INSERT INTO foo (a,b,) VALUES (42,314)", true}, + {"INSERT INTO foo (a,b,) VALUES (42,314,)", true}, + {"CREATE TABLE foo (a uint16, b uint32,)", true}, + {"CREATE TABLE foo (a uint16, b uint32,) -- foo", true}, + // 40 + {"CREATE TABLE foo (a uint16, b uint32,) // foo", true}, + {"CREATE TABLE foo (a uint16, b uint32,) /* foo */", true}, + {"CREATE TABLE foo /* foo */ (a uint16, b uint32,) /* foo */", true}, + {`-- Examples + ALTER TABLE Stock ADD Qty int; + + ALTER TABLE Income DROP COLUMN Taxes; + + CREATE TABLE department + ( + DepartmentID int, + DepartmentName string, // optional comma + ); + + CREATE TABLE employee + ( + LastName string, + DepartmentID int // optional comma + ); + + DROP TABLE Inventory; + + INSERT INTO department (DepartmentID) VALUES (42); + + INSERT INTO department ( + DepartmentName, + DepartmentID, + ) + VALUES ( + "R&D", + 42, + ); + + INSERT INTO department VALUES ( + 42, + "R&D", + ); + + SELECT * FROM Stock; + + SELECT DepartmentID + FROM department + WHERE DepartmentID == 42 + ORDER BY DepartmentName; + + SELECT employee.LastName + FROM department, employee + WHERE department.DepartmentID == employee.DepartmentID + ORDER BY DepartmentID; + + SELECT a.b, c.d + FROM + x AS a, + ( + SELECT * FROM y; // optional semicolon + ) AS c + WHERE a.e > c.e; + + SELECT a.b, c.d + FROM + x AS a, + ( + SELECT * FROM y // no semicolon + ) AS c + WHERE a.e > c.e; + + TRUNCATE TABLE department; + + SELECT DepartmentID + FROM department + WHERE DepartmentID == ?1 + ORDER BY DepartmentName; + + SELECT employee.LastName + FROM department, employee + WHERE department.DepartmentID == $1 && employee.LastName > $2 + ORDER BY DepartmentID; + + `, true}, + {"BEGIN TRANSACTION", true}, + // 45 + {"COMMIT", true}, + {"ROLLBACK", true}, + {` + BEGIN TRANSACTION; + INSERT INTO foo VALUES (42, 3.14); + INSERT INTO foo VALUES (-1, 2.78); + COMMIT;`, true}, + {` + BEGIN TRANSACTION; + INSERT INTO AccountA (Amount) VALUES ($1); + INSERT INTO AccountB (Amount) VALUES (-$1); + COMMIT;`, true}, + {` // A + BEGIN TRANSACTION; + INSERT INTO tmp SELECT * from bar; + SELECT * from tmp; + + // B + ROLLBACK;`, true}, + // 50 + {`-- 6 + ALTER TABLE none DROP COLUMN c1; + `, true}, + } + + for i, test := range table { + //dbg("%d ----\n%q\n----\n", i, test.src) + l := newLexer(test.src) + ok := yyParse(l) == 0 + if g, e := ok, test.ok; g != e { + if !ok { + t.Log(l.errs[0]) + } + t.Error(i, test.src, g, e) + return + } + + switch ok { + case true: + if len(l.errs) != 0 { + t.Fatal(l.errs) + } + case false: + if len(l.errs) == 0 { + t.Fatal(l.errs) + } + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/plan.go b/Godeps/_workspace/src/github.com/cznic/ql/plan.go new file mode 100644 index 000000000..be52b72c6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/plan.go @@ -0,0 +1,2800 @@ +// Copyright 2015 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "bytes" + "fmt" + "strings" + + "github.com/cznic/b" + "github.com/cznic/strutil" +) + +const ( + _ = iota + indexEq // [L] + indexFalse // [false] + indexGe // [L, ...) + indexGt // (L, ...) + indexIsNotNull // (NULL, ...) + indexIsNull // [NULL] + indexIntervalCC // [L, H] + indexIntervalCO // [L, H) + indexIntervalOC // (L, H] + indexIntervalOO // (L, H) + indexLe // (..., H] + indexLt // (..., H) + indexNe // (L) + indexTrue // [true] +) + +// Note: All plans must have a pointer receiver. Enables planA == planB operation. +var ( + _ plan = (*crossJoinDefaultPlan)(nil) + _ plan = (*distinctDefaultPlan)(nil) + _ plan = (*explainDefaultPlan)(nil) + _ plan = (*filterDefaultPlan)(nil) + _ plan = (*fullJoinDefaultPlan)(nil) + _ plan = (*groupByDefaultPlan)(nil) + _ plan = (*indexPlan)(nil) + _ plan = (*leftJoinDefaultPlan)(nil) + _ plan = (*limitDefaultPlan)(nil) + _ plan = (*nullPlan)(nil) + _ plan = (*offsetDefaultPlan)(nil) + _ plan = (*orderByDefaultPlan)(nil) + _ plan = (*rightJoinDefaultPlan)(nil) + _ plan = (*selectFieldsDefaultPlan)(nil) + _ plan = (*selectFieldsGroupPlan)(nil) + _ plan = (*selectIndexDefaultPlan)(nil) + _ plan = (*sysColumnDefaultPlan)(nil) + _ plan = (*sysIndexDefaultPlan)(nil) + _ plan = (*sysTableDefaultPlan)(nil) + _ plan = (*tableDefaultPlan)(nil) + _ plan = (*tableNilPlan)(nil) +) + +type plan interface { + do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error + explain(w strutil.Formatter) + fieldNames() []string + filter(expr expression) (p plan, indicesSought []string, err error) + hasID() bool +} + +func isTableOrIndex(p plan) bool { + switch p.(type) { + case + *indexPlan, + *sysColumnDefaultPlan, + *sysIndexDefaultPlan, + *sysTableDefaultPlan, + *tableDefaultPlan: + return true + default: + return false + } +} + +// Invariants +// - All interval plans produce rows in ascending index value collating order. +// - L <= H +type indexPlan struct { + src *table + cname string + xname string + x btreeIndex + kind int // See interval* consts. + lval interface{} // L, H: Ordered and comparable (lldb perspective). + hval interface{} +} + +func (r *indexPlan) doGe(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ... + // --- --- --- --- --- + + +++ +++ +++ + t := r.src + it, _, err := r.x.Seek([]interface{}{r.lval}) + if err != nil { + return noEOF(err) + } + + for { + _, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doGt(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ... + // --- --- --- --- --- - - +++ +++ +++ + t := r.src + it, _, err := r.x.Seek([]interface{}{r.lval}) + if err != nil { + return noEOF(err) + } + + var ok bool + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + if !ok { + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.lval) == 0 { + continue + } + + ok = true + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doInterval00(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ..., H-1, H-1, H, H, H+1, H+1, ... + // --- --- --- --- --- - - +++ +++ +++ +++ +++ - - --- --- --- + t := r.src + it, _, err := r.x.Seek([]interface{}{r.lval}) + if err != nil { + return noEOF(err) + } + + var ok bool + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + if !ok { + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.lval) == 0 { + continue + } + + ok = true + } + + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.hval) == 0 { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doIntervalOC(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ..., H-1, H-1, H, H, H+1, H+1, ... + // --- --- --- --- --- - - +++ +++ +++ +++ +++ + + --- --- --- + t := r.src + it, _, err := r.x.Seek([]interface{}{r.lval}) + if err != nil { + return noEOF(err) + } + + var ok bool + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + if !ok { + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.lval) == 0 { + continue + } + + ok = true + } + + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.hval) > 0 { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doIntervalCC(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ..., H-1, H-1, H, H, H+1, H+1, ... + // --- --- --- --- --- + + +++ +++ +++ +++ +++ + + --- --- --- + t := r.src + it, _, err := r.x.Seek([]interface{}{r.lval}) + if err != nil { + return noEOF(err) + } + + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.hval) > 0 { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doIntervalCO(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ..., H-1, H-1, H, H, H+1, H+1, ... + // --- --- --- --- --- + + +++ +++ +++ +++ +++ - - --- --- --- + t := r.src + it, _, err := r.x.Seek([]interface{}{r.lval}) + if err != nil { + return noEOF(err) + } + + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.hval) >= 0 { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doLe(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., H-1, H-1, H, H, H+1, H+1, ... + // --- --- +++ +++ +++ + + --- --- + t := r.src + it, err := r.x.SeekFirst() + if err != nil { + return noEOF(err) + } + + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + if k == nil || k[0] == nil { + continue + } + + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.hval) > 0 { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doLt(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., H-1, H-1, H, H, H+1, H+1, ... + // --- --- +++ +++ +++ - - --- --- + t := r.src + it, err := r.x.SeekFirst() + if err != nil { + return noEOF(err) + } + + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + if k == nil || k[0] == nil { + continue + } + + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.hval) >= 0 { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doEq(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ... + // --- --- --- --- --- + + --- --- --- + t := r.src + it, _, err := r.x.Seek([]interface{}{r.lval}) + if err != nil { + return noEOF(err) + } + + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.lval) != 0 { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doNe(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ... + // --- --- +++ +++ +++ - - +++ +++ +++ + t := r.src + it, err := r.x.SeekFirst() + if err != nil { + return noEOF(err) + } + + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + if k == nil || k[0] == nil { + continue + } + + val, err := expand1(k[0], nil) + if err != nil { + return err + } + + if collate1(val, r.hval) == 0 { + continue + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doIsNull(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ... + // +++ +++ --- + t := r.src + it, err := r.x.SeekFirst() + if err != nil { + return noEOF(err) + } + + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + if k != nil && k[0] != nil { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doIsNotNull(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + // nil, nil, ... + // --- --- +++ + t := r.src + it, _, err := r.x.Seek([]interface{}{false}) // lldb collates false right after NULL. + if err != nil { + return noEOF(err) + } + + for { + _, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doFalse(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + t := r.src + it, _, err := r.x.Seek([]interface{}{false}) + if err != nil { + return noEOF(err) + } + + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + b, ok := k[0].(bool) + if !ok || b { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) doTrue(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + t := r.src + it, _, err := r.x.Seek([]interface{}{true}) + if err != nil { + return noEOF(err) + } + + for { + k, h, err := it.Next() + if err != nil { + return noEOF(err) + } + + if _, ok := k[0].(bool); !ok { + return nil + } + + id, data, err := t.row(ctx, h) + if err != nil { + return err + } + + if more, err := f(id, data); err != nil || !more { + return err + } + } +} + +func (r *indexPlan) do(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error { + switch r.kind { + case indexEq: + return r.doEq(ctx, f) + case indexGe: + return r.doGe(ctx, f) + case indexGt: + return r.doGt(ctx, f) + case indexLe: + return r.doLe(ctx, f) + case indexLt: + return r.doLt(ctx, f) + case indexNe: + return r.doNe(ctx, f) + case indexIsNull: + return r.doIsNull(ctx, f) + case indexIsNotNull: + return r.doIsNotNull(ctx, f) + case indexFalse: + return r.doFalse(ctx, f) + case indexTrue: + return r.doTrue(ctx, f) + case indexIntervalOO: + return r.doInterval00(ctx, f) + case indexIntervalCC: + return r.doIntervalCC(ctx, f) + case indexIntervalOC: + return r.doIntervalOC(ctx, f) + case indexIntervalCO: + return r.doIntervalCO(ctx, f) + default: + //dbg("", r.kind) + panic("internal error 072") + } +} + +func (r *indexPlan) explain(w strutil.Formatter) { + s := "" + if r.kind == indexFalse { + s = "!" + } + w.Format("┌Iterate all rows of table %q using index %q where %s%s", r.src.name, r.xname, s, r.cname) + switch r.kind { + case indexEq: + w.Format(" == %v", value{r.lval}) + case indexGe: + w.Format(" >= %v", value{r.lval}) + case indexGt: + w.Format(" > %v", value{r.lval}) + case indexLe: + w.Format(" <= %v", value{r.hval}) + case indexLt: + w.Format(" < %v", value{r.hval}) + case indexNe: + w.Format(" != %v", value{r.lval}) + case indexIsNull: + w.Format(" IS NULL") + case indexIsNotNull: + w.Format(" IS NOT NULL") + case indexFalse, indexTrue: + // nop + case indexIntervalOO: + w.Format(" > %v && %s < %v", value{r.lval}, r.cname, value{r.hval}) + case indexIntervalCC: + w.Format(" >= %v && %s <= %v", value{r.lval}, r.cname, value{r.hval}) + case indexIntervalCO: + w.Format(" >= %v && %s < %v", value{r.lval}, r.cname, value{r.hval}) + case indexIntervalOC: + w.Format(" > %v && %s <= %v", value{r.lval}, r.cname, value{r.hval}) + default: + //dbg("", r.kind) + panic("internal error 073") + } + w.Format("\n└Output field names %v\n", qnames(r.fieldNames())) +} + +func (r *indexPlan) fieldNames() []string { return r.src.fieldNames() } + +func (r *indexPlan) filterEq(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(r.lval, val) == 0 { + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case ge: + if collate1(r.lval, val) >= 0 { + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case '>': + if collate1(r.lval, val) > 0 { + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case le: + if collate1(r.lval, val) <= 0 { + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case '<': + if collate1(r.lval, val) < 0 { + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case neq: + if collate1(r.lval, val) != 0 { + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + } + return nil, nil, nil +} + +func (r *indexPlan) filterGe(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(r.lval, val) <= 0 { + r.lval = val + r.kind = indexEq + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case ge: + if collate1(r.lval, val) < 0 { + r.lval = val + } + return r, nil, nil + case '>': + if collate1(r.lval, val) <= 0 { + r.lval = val + r.kind = indexGt + } + return r, nil, nil + case le: + switch c := collate1(r.lval, val); { + case c < 0: + r.hval = val + r.kind = indexIntervalCC + return r, nil, nil + case c == 0: + r.kind = indexEq + return r, nil, nil + default: // c > 0 + return &nullPlan{r.fieldNames()}, nil, nil + } + case '<': + if collate1(r.lval, val) < 0 { + r.hval = val + r.kind = indexIntervalCO + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case neq: + switch c := collate1(r.lval, val); { + case c < 0: + //MAYBE ORed intervals + case c == 0: + r.kind = indexGt + return r, nil, nil + default: // c > 0 + return r, nil, nil + } + } + return nil, nil, nil +} + +func (r *indexPlan) filterGt(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(r.lval, val) < 0 { + r.lval = val + r.kind = indexEq + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case ge: + if collate1(r.lval, val) < 0 { + r.lval = val + r.kind = indexGe + } + return r, nil, nil + case '>': + if collate1(r.lval, val) < 0 { + r.lval = val + } + return r, nil, nil + case le: + if collate1(r.lval, val) < 0 { + r.hval = val + r.kind = indexIntervalOC + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case '<': + if collate1(r.lval, val) < 0 { + r.hval = val + r.kind = indexIntervalOO + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case neq: + if collate1(r.lval, val) >= 0 { + return r, nil, nil + } + } + return nil, nil, nil +} + +func (r *indexPlan) filterLe(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(r.hval, val) >= 0 { + r.lval = val + r.kind = indexEq + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case ge: + switch c := collate1(r.hval, val); { + case c < 0: + return &nullPlan{r.fieldNames()}, nil, nil + case c == 0: + r.lval = val + r.kind = indexEq + return r, nil, nil + default: // c > 0 + r.lval = val + r.kind = indexIntervalCC + return r, nil, nil + } + case '>': + if collate1(r.hval, val) > 0 { + r.lval = val + r.kind = indexIntervalOC + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case le: + if collate1(r.hval, val) > 0 { + r.hval = val + } + return r, nil, nil + case '<': + if collate1(r.hval, val) >= 0 { + r.hval = val + r.kind = indexLt + } + return r, nil, nil + case neq: + switch c := collate1(r.hval, val); { + case c < 0: + return r, nil, nil + case c == 0: + r.kind = indexLt + return r, nil, nil + default: // c > 0 + //bop + } + } + return nil, nil, nil +} + +func (r *indexPlan) filterLt(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(r.hval, val) > 0 { + r.lval = val + r.kind = indexEq + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case ge: + if collate1(r.hval, val) > 0 { + r.lval = val + r.kind = indexIntervalCO + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case '>': + if collate1(r.hval, val) > 0 { + r.lval = val + r.kind = indexIntervalOO + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case le: + if collate1(r.hval, val) > 0 { + r.hval = val + r.kind = indexLe + } + return r, nil, nil + case '<': + if collate1(r.hval, val) > 0 { + r.hval = val + } + return r, nil, nil + case neq: + if collate1(r.hval, val) > 0 { + return nil, nil, nil + } + + return r, nil, nil + } + return nil, nil, nil +} + +func (r *indexPlan) filterCC(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(val, r.lval) < 0 || collate1(val, r.hval) > 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + r.lval = val + r.kind = indexEq + return r, nil, nil + case ge: + if collate1(val, r.lval) <= 0 { + return r, nil, nil + } + + switch c := collate1(val, r.hval); { + case c < 0: + r.lval = val + return r, nil, nil + case c == 0: + r.lval = val + r.kind = indexEq + return r, nil, nil + default: + return &nullPlan{r.fieldNames()}, nil, nil + } + case '>': + switch c := collate1(val, r.lval); { + case c < 0: + return r, nil, nil + case c == 0: + r.kind = indexIntervalOC + return r, nil, nil + default: + if collate1(val, r.hval) < 0 { + r.lval = val + r.kind = indexIntervalOC + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + } + case le: + switch c := collate1(val, r.lval); { + case c < 0: + return &nullPlan{r.fieldNames()}, nil, nil + case c == 0: + r.kind = indexEq + return r, nil, nil + default: + if collate1(val, r.hval) < 0 { + r.hval = val + } + return r, nil, nil + } + case '<': + if collate1(val, r.lval) <= 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + if collate1(val, r.hval) <= 0 { + r.hval = val + r.kind = indexIntervalCO + } + return r, nil, nil + case neq: + switch c := collate1(val, r.lval); { + case c < 0: + return r, nil, nil + case c == 0: + r.kind = indexIntervalOC + return r, nil, nil + default: + switch c := collate1(val, r.hval); { + case c == 0: + r.kind = indexIntervalCO + return r, nil, nil + case c > 0: + return r, nil, nil + default: + return nil, nil, nil + } + } + } + return nil, nil, nil +} + +func (r *indexPlan) filterOC(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(val, r.lval) <= 0 || collate1(val, r.hval) > 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + r.lval = val + r.kind = indexEq + return r, nil, nil + case ge: + if collate1(val, r.lval) <= 0 { + return r, nil, nil + } + + switch c := collate1(val, r.hval); { + case c < 0: + r.lval = val + r.kind = indexIntervalCC + return r, nil, nil + case c == 0: + r.lval = val + r.kind = indexEq + return r, nil, nil + default: + return &nullPlan{r.fieldNames()}, nil, nil + } + case '>': + if collate1(val, r.lval) <= 0 { + return r, nil, nil + } + + if collate1(val, r.hval) < 0 { + r.lval = val + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case le: + if collate1(val, r.lval) <= 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + if collate1(val, r.hval) < 0 { + r.hval = val + } + return r, nil, nil + case '<': + if collate1(val, r.lval) <= 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + switch c := collate1(val, r.hval); { + case c < 0: + r.hval = val + r.kind = indexIntervalOO + case c == 0: + r.kind = indexIntervalOO + } + return r, nil, nil + case neq: + if collate1(val, r.lval) <= 0 { + return r, nil, nil + } + + switch c := collate1(val, r.hval); { + case c < 0: + return nil, nil, nil + case c == 0: + r.kind = indexIntervalOO + return r, nil, nil + default: + return r, nil, nil + } + } + return nil, nil, nil +} + +func (r *indexPlan) filterOO(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(val, r.lval) <= 0 || collate1(val, r.hval) >= 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + r.lval = val + r.kind = indexEq + return r, nil, nil + case ge: + if collate1(val, r.lval) <= 0 { + return r, nil, nil + } + + if collate1(val, r.hval) < 0 { + r.lval = val + r.kind = indexIntervalCO + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case '>': + if collate1(val, r.lval) <= 0 { + return r, nil, nil + } + + if collate1(val, r.hval) < 0 { + r.lval = val + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case le: + if collate1(val, r.lval) <= 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + if collate1(val, r.hval) < 0 { + r.hval = val + r.kind = indexIntervalOC + } + return r, nil, nil + case '<': + if collate1(val, r.lval) <= 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + if collate1(val, r.hval) < 0 { + r.hval = val + } + return r, nil, nil + case neq: + if collate1(val, r.lval) <= 0 || collate1(val, r.hval) >= 0 { + return r, nil, nil + } + + return nil, nil, nil + } + return nil, nil, nil +} + +func (r *indexPlan) filterCO(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(val, r.lval) < 0 || collate1(val, r.hval) >= 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + r.lval = val + r.kind = indexEq + return r, nil, nil + case ge: + if collate1(val, r.lval) <= 0 { + return r, nil, nil + } + + if collate1(val, r.hval) < 0 { + r.lval = val + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case '>': + switch c := collate1(val, r.lval); { + case c < 0: + return r, nil, nil + case c == 0: + r.kind = indexIntervalOO + return r, nil, nil + default: + if collate1(val, r.hval) < 0 { + r.lval = val + r.kind = indexIntervalOO + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + } + case le: + switch c := collate1(val, r.lval); { + case c < 0: + return &nullPlan{r.fieldNames()}, nil, nil + case c == 0: + r.kind = indexEq + return r, nil, nil + default: + if collate1(val, r.hval) < 0 { + r.hval = val + r.kind = indexIntervalCC + } + return r, nil, nil + } + case '<': + if collate1(val, r.lval) <= 0 { + return &nullPlan{r.fieldNames()}, nil, nil + } + + if collate1(val, r.hval) < 0 { + r.hval = val + } + return r, nil, nil + case neq: + switch c := collate1(val, r.lval); { + case c < 0: + return r, nil, nil + case c == 0: + r.kind = indexIntervalOO + return r, nil, nil + default: + if collate1(val, r.hval) < 0 { + return nil, nil, nil + } + + return r, nil, nil + } + } + return nil, nil, nil +} + +func (r *indexPlan) filterNe(binOp2 int, val interface{}) (plan, []string, error) { + switch binOp2 { + case eq: + if collate1(val, r.lval) != 0 { + r.lval = val + r.kind = indexEq + return r, nil, nil + } + + return &nullPlan{r.fieldNames()}, nil, nil + case ge: + switch c := collate1(val, r.lval); { + case c < 0: + return nil, nil, nil //TODO + case c == 0: + r.kind = indexGt + return r, nil, nil + default: + r.lval = val + r.kind = indexGe + return r, nil, nil + } + case '>': + if collate1(val, r.lval) < 0 { + return nil, nil, nil //TODO + } + + r.lval = val + r.kind = indexGt + return r, nil, nil + case le: + switch c := collate1(val, r.lval); { + case c < 0: + r.hval = val + r.kind = indexLe + return r, nil, nil + case c == 0: + r.kind = indexLt + return r, nil, nil + default: + return nil, nil, nil //TODO + } + case '<': + if collate1(val, r.lval) <= 0 { + r.hval = val + r.kind = indexLt + return r, nil, nil + } + + return nil, nil, nil //TODO + case neq: + if collate1(val, r.lval) == 0 { + return r, nil, nil + } + + return nil, nil, nil + } + return nil, nil, nil +} + +func (r *indexPlan) filter(expr expression) (plan, []string, error) { + switch x := expr.(type) { + case *binaryOperation: + ok, cname, val, err := x.isIdentRelOpVal() + if err != nil { + return nil, nil, err + } + + if !ok || r.cname != cname { + break + } + + if val, err = typeCheck1(val, findCol(r.src.cols, cname)); err != nil { + return nil, nil, err + } + + switch r.kind { + case indexEq: // [L] + return r.filterEq(x.op, val) + case indexGe: // [L, ...) + return r.filterGe(x.op, val) + case indexGt: // (L, ...) + return r.filterGt(x.op, val) + case indexIntervalCC: // [L, H] + return r.filterCC(x.op, val) + case indexIntervalCO: // [L, H) + return r.filterCO(x.op, val) + case indexIntervalOC: // (L, H] + return r.filterOC(x.op, val) + case indexIntervalOO: // (L, H) + return r.filterOO(x.op, val) + case indexLe: // (..., H] + return r.filterLe(x.op, val) + case indexLt: // (..., H) + return r.filterLt(x.op, val) + case indexNe: // (L) + return r.filterNe(x.op, val) + } + case *ident: + cname := x.s + if r.cname != cname { + break + } + + switch r.kind { + case indexFalse: // [false] + return &nullPlan{r.fieldNames()}, nil, nil + case indexTrue: // [true] + return r, nil, nil + } + case *unaryOperation: + if x.op != '!' { + break + } + + operand, ok := x.v.(*ident) + if !ok { + break + } + + cname := operand.s + if r.cname != cname { + break + } + + switch r.kind { + case indexFalse: // [false] + return r, nil, nil + case indexTrue: // [true] + return &nullPlan{r.fieldNames()}, nil, nil + } + } + + return nil, nil, nil +} + +func (r *indexPlan) hasID() bool { return true } + +type explainDefaultPlan struct { + s stmt +} + +func (r *explainDefaultPlan) hasID() bool { return false } + +func (r *explainDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error { + var buf bytes.Buffer + switch x := r.s.(type) { + default: + w := strutil.IndentFormatter(&buf, "│ ") + x.explain(ctx, w) + } + + a := bytes.Split(buf.Bytes(), []byte{'\n'}) + for _, v := range a[:len(a)-1] { + if more, err := f(nil, []interface{}{string(v)}); !more || err != nil { + return err + } + } + return nil +} + +func (r *explainDefaultPlan) explain(w strutil.Formatter) { + return +} + +func (r *explainDefaultPlan) fieldNames() []string { + return []string{""} +} + +func (r *explainDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +type filterDefaultPlan struct { + plan + expr expression + is []string +} + +func (r *filterDefaultPlan) hasID() bool { return r.plan.hasID() } + +func (r *filterDefaultPlan) explain(w strutil.Formatter) { + r.plan.explain(w) + w.Format("┌Filter on %v\n", r.expr) + if len(r.is) != 0 { + w.Format("│Possibly useful indices\n") + m := map[string]bool{} + for _, v := range r.is { + if !m[v] { + m[v] = true + n := "" + for _, r := range v { + if r >= '0' && r <= '9' || r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r == '_' { + n += string(r) + continue + } + + n += "_" + } + for strings.Contains(n, "__") { + n = strings.Replace(n, "__", "_", -1) + } + for strings.HasSuffix(n, "_") { + n = n[:len(n)-1] + } + w.Format("│CREATE INDEX x%s ON %s;\n", n, v) + } + } + } + w.Format("└Output field names %v\n", qnames(r.plan.fieldNames())) +} + +func (r *filterDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) { + m := map[interface{}]interface{}{} + fields := r.plan.fieldNames() + return r.plan.do(ctx, func(rid interface{}, data []interface{}) (bool, error) { + for i, v := range fields { + m[v] = data[i] + } + m["$id"] = rid + val, err := r.expr.eval(ctx, m) + if err != nil { + return false, err + } + + if val == nil { + return true, nil + } + + x, ok := val.(bool) + if !ok { + return false, fmt.Errorf("invalid boolean expression %s (value of type %T)", val, val) + } + + if !x { + return true, nil + } + + return f(rid, data) + }) +} + +type crossJoinDefaultPlan struct { + rsets []plan + names []string + fields []string +} + +func (r *crossJoinDefaultPlan) hasID() bool { return false } + +func (r *crossJoinDefaultPlan) explain(w strutil.Formatter) { + w.Format("┌Compute Cartesian product of%i\n") + for i, v := range r.rsets { + sel := !isTableOrIndex(v) + if sel { + w.Format("┌Iterate all rows of virtual table %q%i\n", r.names[i]) + } + v.explain(w) + if sel { + w.Format("%u└Output field names %v\n", qnames(v.fieldNames())) + } + } + w.Format("%u└Output field names %v\n", qnames(r.fields)) +} + +func (r *crossJoinDefaultPlan) filter(expr expression) (plan, []string, error) { + var is []string + for i, v := range r.names { + e2, err := expr.clone(nil, v) + if err != nil { + return nil, nil, err + } + + p2, is2, err := r.rsets[i].filter(e2) + is = append(is, is2...) + if err != nil { + return nil, nil, err + } + + if p2 != nil { + r.rsets[i] = p2 + return r, is, nil + } + } + return nil, is, nil +} + +func (r *crossJoinDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error { + if len(r.rsets) == 1 { + return r.rsets[0].do(ctx, f) + } + + ids := map[string]interface{}{} + var g func([]interface{}, []plan, int) error + g = func(prefix []interface{}, rsets []plan, x int) (err error) { + return rsets[0].do(ctx, func(id interface{}, in []interface{}) (bool, error) { + ids[r.names[x]] = id + if len(rsets) > 1 { + return true, g(append(prefix, in...), rsets[1:], x+1) + } + + return f(ids, append(prefix, in...)) + }) + } + return g(nil, r.rsets, 0) +} + +func (r *crossJoinDefaultPlan) fieldNames() []string { return r.fields } + +type distinctDefaultPlan struct { + src plan + fields []string +} + +func (r *distinctDefaultPlan) hasID() bool { return false } + +func (r *distinctDefaultPlan) explain(w strutil.Formatter) { + r.src.explain(w) + w.Format("┌Compute distinct rows\n└Output field names %v\n", r.fields) +} + +func (r *distinctDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *distinctDefaultPlan) fieldNames() []string { return r.fields } + +func (r *distinctDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) { + t, err := ctx.db.store.CreateTemp(true) + if err != nil { + return + } + + defer func() { + if derr := t.Drop(); derr != nil && err == nil { + err = derr + } + }() + + if err = r.src.do(ctx, func(id interface{}, in []interface{}) (bool, error) { + if err = t.Set(in, nil); err != nil { + return false, err + } + + return true, nil + }); err != nil { + return + } + + var data []interface{} + more := true + it, err := t.SeekFirst() + for more && err == nil { + data, _, err = it.Next() + if err != nil { + break + } + + more, err = f(nil, data) + } + return noEOF(err) +} + +type groupByDefaultPlan struct { + colNames []string + src plan + fields []string +} + +func (r *groupByDefaultPlan) hasID() bool { return false } + +func (r *groupByDefaultPlan) explain(w strutil.Formatter) { + r.src.explain(w) + switch { + case len(r.colNames) == 0: //TODO this case should not exist for this plan, should become tableDefaultPlan + w.Format("┌Group by distinct rows") + default: + w.Format("┌Group by") + for _, v := range r.colNames { + w.Format(" %s,", v) + } + } + w.Format("\n└Output field names %v\n", qnames(r.fields)) +} + +func (r *groupByDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *groupByDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) { + t, err := ctx.db.store.CreateTemp(true) + if err != nil { + return + } + + defer func() { + if derr := t.Drop(); derr != nil && err == nil { + err = derr + } + }() + + var gcols []*col + var cols []*col + m := map[string]int{} + for i, v := range r.src.fieldNames() { + m[v] = i + } + for _, c := range r.colNames { + i, ok := m[c] + if !ok { + return fmt.Errorf("unknown field %s", c) + } + + gcols = append(gcols, &col{name: c, index: i}) + } + k := make([]interface{}, len(r.colNames)) //TODO optimize when len(r.cols) == 0, should become tableDefaultPlan + if err = r.src.do(ctx, func(rid interface{}, in []interface{}) (more bool, err error) { + infer(in, &cols) + for i, c := range gcols { + k[i] = in[c.index] + } + h0, err := t.Get(k) + if err != nil { + return false, err + } + + var h int64 + if len(h0) != 0 { + h, _ = h0[0].(int64) + } + nh, err := t.Create(append([]interface{}{h, nil}, in...)...) + if err != nil { + return false, err + } + + for i, c := range gcols { + k[i] = in[c.index] + } + err = t.Set(k, []interface{}{nh}) + if err != nil { + return false, err + } + + return true, nil + }); err != nil { + return + } + + for i, v := range r.src.fieldNames()[:len(cols)] { + cols[i].name = v + cols[i].index = i + } + if more, err := f(nil, []interface{}{t, cols}); !more || err != nil { + return err + } + + it, err := t.SeekFirst() + more := true + var data []interface{} + for more && err == nil { + if _, data, err = it.Next(); err != nil { + break + } + + more, err = f(nil, data) + } + return noEOF(err) +} + +func (r *groupByDefaultPlan) fieldNames() []string { return r.fields } + +type selectIndexDefaultPlan struct { + nm string + x interface{} +} + +func (r *selectIndexDefaultPlan) hasID() bool { return false } + +func (r *selectIndexDefaultPlan) explain(w strutil.Formatter) { + w.Format("┌Iterate all values of index %q\n└Output field names N/A\n", r.nm) +} + +func (r *selectIndexDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *selectIndexDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) { + var x btreeIndex + switch ix := r.x.(type) { + case *indexedCol: + x = ix.x + case *index2: + x = ix.x + default: + panic("internal error 007") + } + + en, err := x.SeekFirst() + if err != nil { + return noEOF(err) + } + + var id int64 + for { + k, _, err := en.Next() + if err != nil { + return noEOF(err) + } + + id++ + if more, err := f(id, k); !more || err != nil { + return err + } + } +} + +func (r *selectIndexDefaultPlan) fieldNames() []string { + return []string{r.nm} +} + +type limitDefaultPlan struct { + expr expression + src plan + fields []string +} + +func (r *limitDefaultPlan) hasID() bool { return r.src.hasID() } + +func (r *limitDefaultPlan) explain(w strutil.Formatter) { + r.src.explain(w) + w.Format("┌Pass first %v records\n└Output field names %v\n", r.expr, r.fields) +} + +func (r *limitDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *limitDefaultPlan) fieldNames() []string { return r.fields } + +func (r *limitDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) (err error) { + m := map[interface{}]interface{}{} + var eval bool + var lim uint64 + return r.src.do(ctx, func(rid interface{}, in []interface{}) (more bool, err error) { + if !eval { + for i, fld := range r.fields { + if fld != "" { + m[fld] = in[i] + } + } + m["$id"] = rid + val, err := r.expr.eval(ctx, m) + if err != nil { + return false, err + } + + if val == nil { + return true, nil + } + + if lim, err = limOffExpr(val); err != nil { + return false, err + } + + eval = true + } + switch lim { + case 0: + return false, nil + default: + lim-- + return f(rid, in) + } + }) +} + +type offsetDefaultPlan struct { + expr expression + src plan + fields []string +} + +func (r *offsetDefaultPlan) hasID() bool { return r.src.hasID() } + +func (r *offsetDefaultPlan) explain(w strutil.Formatter) { + r.src.explain(w) + w.Format("┌Skip first %v records\n└Output field names %v\n", r.expr, qnames(r.fields)) +} + +func (r *offsetDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *offsetDefaultPlan) fieldNames() []string { return r.fields } + +func (r *offsetDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error { + m := map[interface{}]interface{}{} + var eval bool + var off uint64 + return r.src.do(ctx, func(rid interface{}, in []interface{}) (bool, error) { + if !eval { + for i, fld := range r.fields { + if fld != "" { + m[fld] = in[i] + } + } + m["$id"] = rid + val, err := r.expr.eval(ctx, m) + if err != nil { + return false, err + } + + if val == nil { + return true, nil + } + + if off, err = limOffExpr(val); err != nil { + return false, err + } + + eval = true + } + if off > 0 { + off-- + return true, nil + } + + return f(rid, in) + }) +} + +type orderByDefaultPlan struct { + asc bool + by []expression + src plan + fields []string +} + +func (r *orderByDefaultPlan) hasID() bool { return r.src.hasID() } + +func (r *orderByDefaultPlan) explain(w strutil.Formatter) { + r.src.explain(w) + w.Format("┌Order%s by", map[bool]string{false: " descending"}[r.asc]) + for _, v := range r.by { + w.Format(" %s,", v) + } + w.Format("\n└Output field names %v\n", qnames(r.fields)) +} + +func (r *orderByDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *orderByDefaultPlan) fieldNames() []string { return r.fields } + +func (r *orderByDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) { + t, err := ctx.db.store.CreateTemp(r.asc) + if err != nil { + return + } + + defer func() { + if derr := t.Drop(); derr != nil && err == nil { + err = derr + } + }() + + m := map[interface{}]interface{}{} + flds := r.fields + k := make([]interface{}, len(r.by)+1) + id := int64(-1) + if err = r.src.do(ctx, func(rid interface{}, in []interface{}) (bool, error) { + id++ + for i, fld := range flds { + if fld != "" { + m[fld] = in[i] + } + } + m["$id"] = rid + for i, expr := range r.by { + val, err := expr.eval(ctx, m) + if err != nil { + return false, err + } + + if val != nil { + val, ordered, err := isOrderedType(val) + if err != nil { + return false, err + } + + if !ordered { + return false, fmt.Errorf("cannot order by %v (type %T)", val, val) + + } + } + + k[i] = val + } + k[len(r.by)] = id + if err = t.Set(k, in); err != nil { + return false, err + } + + return true, nil + }); err != nil { + return + } + + it, err := t.SeekFirst() + if err != nil { + return noEOF(err) + } + + var data []interface{} + more := true + for more && err == nil { + if _, data, err = it.Next(); err != nil { + break + } + + more, err = f(nil, data) + } + return noEOF(err) +} + +type selectFieldsDefaultPlan struct { + flds []*fld + src plan + fields []string +} + +func (r *selectFieldsDefaultPlan) hasID() bool { return r.src.hasID() } + +func (r *selectFieldsDefaultPlan) explain(w strutil.Formatter) { + //TODO check for non existing fields + r.src.explain(w) + w.Format("┌Evaluate") + for _, v := range r.flds { + w.Format(" %s as %s,", v.expr, fmt.Sprintf("%q", v.name)) + } + w.Format("\n└Output field names %v\n", qnames(r.fields)) +} + +func (r *selectFieldsDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *selectFieldsDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error { + fields := r.src.fieldNames() + m := map[interface{}]interface{}{} + return r.src.do(ctx, func(rid interface{}, in []interface{}) (bool, error) { + for i, nm := range fields { + if nm != "" { + m[nm] = in[i] + } + } + m["$id"] = rid + out := make([]interface{}, len(r.flds)) + for i, fld := range r.flds { + var err error + if out[i], err = fld.expr.eval(ctx, m); err != nil { + return false, err + } + } + return f(rid, out) + }) +} + +func (r *selectFieldsDefaultPlan) fieldNames() []string { return r.fields } + +type selectFieldsGroupPlan struct { + flds []*fld + src *groupByDefaultPlan + fields []string +} + +func (r *selectFieldsGroupPlan) hasID() bool { return false } + +func (r *selectFieldsGroupPlan) explain(w strutil.Formatter) { + //TODO check for non existing fields + r.src.explain(w) + w.Format("┌Evaluate") + for _, v := range r.flds { + w.Format(" %s as %s,", v.expr, fmt.Sprintf("%q", v.name)) + } + w.Format("\n└Output field names %v\n", qnames(r.fields)) +} + +func (r *selectFieldsGroupPlan) fieldNames() []string { return r.fields } + +func (r *selectFieldsGroupPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *selectFieldsGroupPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error { + var t temp + var cols []*col + var err error + out := make([]interface{}, len(r.flds)) + ok := false + rows := false + if err = r.src.do(ctx, func(rid interface{}, in []interface{}) (bool, error) { + if ok { + h := in[0].(int64) + m := map[interface{}]interface{}{} + for h != 0 { + in, err = t.Read(nil, h, cols...) + if err != nil { + return false, err + } + + rec := in[2:] + for i, c := range cols { + if nm := c.name; nm != "" { + m[nm] = rec[i] + } + } + m["$id"] = rid + for _, fld := range r.flds { + if _, err = fld.expr.eval(ctx, m); err != nil { + return false, err + } + } + + h = in[0].(int64) + } + m["$agg"] = true + for i, fld := range r.flds { + if out[i], err = fld.expr.eval(ctx, m); err != nil { + return false, err + } + } + rows = true + return f(nil, out) + } + + ok = true + t = in[0].(temp) + cols = in[1].([]*col) + if len(r.flds) == 0 { // SELECT * + r.flds = make([]*fld, len(cols)) + for i, v := range cols { + r.flds[i] = &fld{expr: &ident{v.name}, name: v.name} + } + out = make([]interface{}, len(r.flds)) + } + return true, nil + }); err != nil { + return err + } + + if rows { + return nil + } + + m := map[interface{}]interface{}{"$agg0": true} // aggregate empty record set + for i, fld := range r.flds { + if out[i], err = fld.expr.eval(ctx, m); err != nil { + return err + } + } + _, err = f(nil, out) + + return err +} + +type sysColumnDefaultPlan struct{} + +func (r *sysColumnDefaultPlan) hasID() bool { return false } + +func (r *sysColumnDefaultPlan) explain(w strutil.Formatter) { + w.Format("┌Iterate all rows of table \"__Column\"\n└Output field names %v\n", qnames(r.fieldNames())) +} + +func (r *sysColumnDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *sysColumnDefaultPlan) fieldNames() []string { + return []string{"TableName", "Ordinal", "Name", "Type"} +} + +func (r *sysColumnDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error { + rec := make([]interface{}, 4) + di, err := ctx.db.info() + if err != nil { + return err + } + + var id int64 + for _, ti := range di.Tables { + rec[0] = ti.Name + var ix int64 + for _, ci := range ti.Columns { + ix++ + rec[1] = ix + rec[2] = ci.Name + rec[3] = ci.Type.String() + id++ + if more, err := f(id, rec); !more || err != nil { + return err + } + } + } + return nil +} + +type sysIndexDefaultPlan struct{} + +func (r *sysIndexDefaultPlan) hasID() bool { return false } + +func (r *sysIndexDefaultPlan) explain(w strutil.Formatter) { + w.Format("┌Iterate all rows of table \"__Index\"\n└Output field names %v\n", qnames(r.fieldNames())) +} + +func (r *sysIndexDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *sysIndexDefaultPlan) fieldNames() []string { + return []string{"TableName", "ColumnName", "Name", "IsUnique"} +} + +func (r *sysIndexDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error { + rec := make([]interface{}, 4) + di, err := ctx.db.info() + if err != nil { + return err + } + + var id int64 + for _, xi := range di.Indices { + rec[0] = xi.Table + rec[1] = xi.Column + rec[2] = xi.Name + rec[3] = xi.Unique + id++ + if more, err := f(id, rec); !more || err != nil { + return err + } + } + return nil +} + +type sysTableDefaultPlan struct{} + +func (r *sysTableDefaultPlan) hasID() bool { return false } + +func (r *sysTableDefaultPlan) explain(w strutil.Formatter) { + w.Format("┌Iterate all rows of table \"__Table\"\n└Output field names %v\n", qnames(r.fieldNames())) +} + +func (r *sysTableDefaultPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *sysTableDefaultPlan) fieldNames() []string { return []string{"Name", "Schema"} } + +func (r *sysTableDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error { + rec := make([]interface{}, 2) + di, err := ctx.db.info() + if err != nil { + return err + } + + var id int64 + for _, ti := range di.Tables { + rec[0] = ti.Name + a := []string{} + for _, ci := range ti.Columns { + s := "" + if ci.NotNull { + s += " NOT NULL" + } + if c := ci.Constraint; c != "" { + s += " " + c + } + if d := ci.Default; d != "" { + s += " DEFAULT " + d + } + a = append(a, fmt.Sprintf("%s %s%s", ci.Name, ci.Type, s)) + } + rec[1] = fmt.Sprintf("CREATE TABLE %s (%s);", ti.Name, strings.Join(a, ", ")) + id++ + if more, err := f(id, rec); !more || err != nil { + return err + } + } + return nil +} + +type tableNilPlan struct { + t *table +} + +func (r *tableNilPlan) hasID() bool { return true } + +func (r *tableNilPlan) explain(w strutil.Formatter) { + w.Format("┌Iterate all rows of table %q\n└Output field names %v\n", r.t.name, qnames(r.fieldNames())) +} + +func (r *tableNilPlan) fieldNames() []string { return []string{} } + +func (r *tableNilPlan) filter(expr expression) (plan, []string, error) { + return nil, nil, nil +} + +func (r *tableNilPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) { + t := r.t + h := t.head + cols := t.cols + for h > 0 { + rec, err := t.store.Read(nil, h, cols...) // 0:next, 1:id, 2...: data + if err != nil { + return err + } + + if m, err := f(rec[1], nil); !m || err != nil { + return err + } + + h = rec[0].(int64) // next + } + return nil +} + +type tableDefaultPlan struct { + t *table + fields []string +} + +func (r *tableDefaultPlan) hasID() bool { return true } + +func (r *tableDefaultPlan) explain(w strutil.Formatter) { + w.Format("┌Iterate all rows of table %q\n└Output field names %v\n", r.t.name, qnames(r.fields)) +} + +func (r *tableDefaultPlan) filterBinOp(x *binaryOperation) (plan, []string, error) { + ok, cn, rval, err := x.isIdentRelOpVal() + if err != nil { + return nil, nil, err + } + + if !ok { + return nil, nil, nil + } + + t := r.t + c, ix := t.findIndexByColName(cn) + if ix == nil { // Column cn has no index. + return nil, []string{fmt.Sprintf("%s(%s)", t.name, cn)}, nil + } + + if rval, err = typeCheck1(rval, c); err != nil { + return nil, nil, err + } + + switch x.op { + case eq: + return &indexPlan{t, cn, ix.name, ix.x, indexEq, rval, rval}, nil, nil + case '<': + return &indexPlan{t, cn, ix.name, ix.x, indexLt, nil, rval}, nil, nil + case le: + return &indexPlan{t, cn, ix.name, ix.x, indexLe, nil, rval}, nil, nil + case ge: + return &indexPlan{t, cn, ix.name, ix.x, indexGe, rval, nil}, nil, nil + case '>': + return &indexPlan{t, cn, ix.name, ix.x, indexGt, rval, nil}, nil, nil + case neq: + return &indexPlan{t, cn, ix.name, ix.x, indexNe, rval, rval}, nil, nil + default: + panic("internal error 069") + } +} + +func (r *tableDefaultPlan) filterIdent(x *ident, trueValue bool) (plan, []string, error) { + cn := x.s + t := r.t + for _, v := range t.cols { + if v.name != cn { + continue + } + + if v.typ != qBool { + return nil, nil, nil + } + + xi := v.index + 1 // 0: id() + if xi >= len(t.indices) { + return nil, nil, nil + } + + ix := t.indices[xi] + if ix == nil { // Column cn has no index. + return nil, []string{fmt.Sprintf("%s(%s)", t.name, cn)}, nil + } + + kind := indexFalse + if trueValue { + kind = indexTrue + } + return &indexPlan{t, cn, ix.name, ix.x, kind, nil, nil}, nil, nil + } + return nil, nil, nil +} + +func (r *tableDefaultPlan) filterIsNull(x *isNull) (plan, []string, error) { + ok, cn := isColumnExpression(x.expr) + if !ok { + return nil, nil, nil + } + + t := r.t + _, ix := t.findIndexByColName(cn) + if ix == nil { // Column cn has no index. + return nil, []string{fmt.Sprintf("%s(%s)", t.name, cn)}, nil + } + + switch { + case x.not: + return &indexPlan{t, cn, ix.name, ix.x, indexIsNotNull, nil, nil}, nil, nil + default: + return &indexPlan{t, cn, ix.name, ix.x, indexIsNull, nil, nil}, nil, nil + } +} + +func (r *tableDefaultPlan) filter(expr expression) (plan, []string, error) { + cols := mentionedColumns(expr) + for _, v := range r.fields { + delete(cols, v) + } + for k := range cols { + return nil, nil, fmt.Errorf("unknown field %s", k) + } + + var is []string + + //TODO var sexpr string + //TODO for _, ix := range t.indices2 { + //TODO if len(ix.exprList) != 1 { + //TODO continue + //TODO } + + //TODO if sexpr == "" { + //TODO sexpr = expr.String() + //TODO } + //TODO if ix.sources[0] != sexpr { + //TODO continue + //TODO } + + //TODO } + + switch x := expr.(type) { + case *binaryOperation: + return r.filterBinOp(x) + case *ident: + return r.filterIdent(x, true) + case *isNull: + return r.filterIsNull(x) + case *unaryOperation: + if x.op != '!' { + break + } + + if operand, ok := x.v.(*ident); ok { + return r.filterIdent(operand, false) + } + default: + //dbg("", expr) + return nil, is, nil //TODO + } + + return nil, is, nil +} + +func (r *tableDefaultPlan) do(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) (err error) { + t := r.t + cols := t.cols + h := t.head + for h > 0 { + rec, err := t.row0(ctx, h) + if err != nil { + return err + } + + h = rec[0].(int64) + id := rec[1].(int64) + for i, c := range cols { + rec[i] = rec[c.index+2] + } + if m, err := f(id, rec[:len(cols)]); !m || err != nil { + return err + } + } + return nil +} + +func (r *tableDefaultPlan) fieldNames() []string { return r.fields } + +type nullPlan struct { + fields []string +} + +func (r *nullPlan) hasID() bool { return false } + +func (r *nullPlan) fieldNames() []string { return r.fields } + +func (r *nullPlan) explain(w strutil.Formatter) { + w.Format("┌Iterate no rows\n└Output field names %v\n", qnames(r.fields)) +} + +func (r *nullPlan) do(*execCtx, func(interface{}, []interface{}) (bool, error)) error { + return nil +} + +func (r *nullPlan) filter(expr expression) (plan, []string, error) { + return r, nil, nil +} + +type leftJoinDefaultPlan struct { + on expression + rsets []plan + names []string + right int + fields []string +} + +func (r *leftJoinDefaultPlan) hasID() bool { return false } + +func (r *leftJoinDefaultPlan) explain(w strutil.Formatter) { + w.Format("┌Compute Cartesian product of%i\n") + for i, v := range r.rsets { + sel := !isTableOrIndex(v) + if sel { + w.Format("┌Iterate all rows of virtual table %q%i\n", r.names[i]) + } + v.explain(w) + if sel { + w.Format("%u└Output field names %v\n", qnames(v.fieldNames())) + } + } + w.Format("Extend the product with all NULL rows of %q when no match for %v%u\n", r.names[len(r.names)-1], r.on) + w.Format("└Output field names %v\n", qnames(r.fields)) +} + +func (r *leftJoinDefaultPlan) filter(expr expression) (plan, []string, error) { + var is []string + for i, v := range r.names { + e2, err := expr.clone(nil, v) + if err != nil { + return nil, nil, err + } + + p2, is2, err := r.rsets[i].filter(e2) + is = append(is, is2...) + if err != nil { + return nil, nil, err + } + + if p2 != nil { + r.rsets[i] = p2 + return r, is, nil + } + } + return nil, is, nil +} + +type rightJoinDefaultPlan struct { + leftJoinDefaultPlan +} + +func (r *rightJoinDefaultPlan) hasID() bool { return false } + +func (r *rightJoinDefaultPlan) explain(w strutil.Formatter) { + w.Format("┌Compute Cartesian product of%i\n") + for i, v := range r.rsets { + sel := !isTableOrIndex(v) + if sel { + w.Format("┌Iterate all rows of virtual table %q%i\n", r.names[i]) + } + v.explain(w) + if sel { + w.Format("%u└Output field names %v\n", qnames(v.fieldNames())) + } + } + w.Format("Extend the product with all NULL rows of all but %q when no match for %v%u\n", r.names[len(r.names)-1], r.on) + w.Format("└Output field names %v\n", qnames(r.fields)) +} + +func (r *rightJoinDefaultPlan) filter(expr expression) (plan, []string, error) { + var is []string + for i, v := range r.names { + e2, err := expr.clone(nil, v) + if err != nil { + return nil, nil, err + } + + p2, is2, err := r.rsets[i].filter(e2) + is = append(is, is2...) + if err != nil { + return nil, nil, err + } + + if p2 != nil { + r.rsets[i] = p2 + return r, is, nil + } + } + return nil, is, nil +} + +type fullJoinDefaultPlan struct { + leftJoinDefaultPlan +} + +func (r *fullJoinDefaultPlan) hasID() bool { return false } + +func (r *fullJoinDefaultPlan) explain(w strutil.Formatter) { + w.Format("┌Compute Cartesian product of%i\n") + for i, v := range r.rsets { + sel := !isTableOrIndex(v) + if sel { + w.Format("┌Iterate all rows of virtual table %q%i\n", r.names[i]) + } + v.explain(w) + if sel { + w.Format("%u└Output field names %v\n", qnames(v.fieldNames())) + } + } + w.Format("Extend the product with all NULL rows of %q when no match for %v\n", r.names[len(r.names)-1], r.on) + w.Format("Extend the product with all NULL rows of all but %q when no match for %v%u\n", r.names[len(r.names)-1], r.on) + w.Format("└Output field names %v\n", qnames(r.fields)) +} + +func (r *fullJoinDefaultPlan) filter(expr expression) (plan, []string, error) { + var is []string + for i, v := range r.names { + e2, err := expr.clone(nil, v) + if err != nil { + return nil, nil, err + } + + p2, is2, err := r.rsets[i].filter(e2) + is = append(is, is2...) + if err != nil { + return nil, nil, err + } + + if p2 != nil { + r.rsets[i] = p2 + return r, is, nil + } + } + return nil, is, nil +} + +func (r *leftJoinDefaultPlan) fieldNames() []string { return r.fields } + +func (r *leftJoinDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error { + m := map[interface{}]interface{}{} + ids := map[string]interface{}{} + var g func([]interface{}, []plan, int) error + var match bool + g = func(prefix []interface{}, rsets []plan, x int) (err error) { + return rsets[0].do(ctx, func(id interface{}, in []interface{}) (bool, error) { + ids[r.names[x]] = id + row := append(prefix, in...) + if len(rsets) > 1 { + if len(rsets) == 2 { + match = false + } + if err = g(row, rsets[1:], x+1); err != nil { + return false, err + } + + if len(rsets) != 2 || match { + return true, nil + } + + ids[r.names[x+1]] = nil + return f(ids, append(row, make([]interface{}, r.right)...)) + } + + for i, fld := range r.fields { + if fld != "" { + m[fld] = row[i] + } + } + + val, err := r.on.eval(ctx, m) + if err != nil { + return false, err + } + + if val == nil { + return true, nil + } + + x, ok := val.(bool) + if !ok { + return false, fmt.Errorf("invalid ON expression %s (value of type %T)", val, val) + } + + if !x { + return true, nil + } + + match = true + return f(ids, row) + }) + } + return g(nil, r.rsets, 0) +} + +func (r *rightJoinDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error { + right := r.right + left := len(r.fields) - right + n := len(r.rsets) + m := map[interface{}]interface{}{} + ids := map[string]interface{}{} + var g func([]interface{}, []plan, int) error + var match bool + nf := len(r.fields) + fields := append(append([]string(nil), r.fields[nf-right:]...), r.fields[:nf-right]...) + g = func(prefix []interface{}, rsets []plan, x int) (err error) { + return rsets[0].do(ctx, func(id interface{}, in []interface{}) (bool, error) { + ids[r.names[x]] = id + row := append(prefix, in...) + if len(rsets) > 1 { + if len(rsets) == n { + match = false + } + if err = g(row, rsets[1:], x+1); err != nil { + return false, err + } + + if len(rsets) != n || match { + return true, nil + } + + for i := 0; i < n-1; i++ { + ids[r.names[i]] = nil + } + + // rigth, left -> left, right + return f(ids, append(make([]interface{}, left), row[:right]...)) + } + + for i, fld := range fields { + if fld != "" { + m[fld] = row[i] + } + } + + val, err := r.on.eval(ctx, m) + if err != nil { + return false, err + } + + if val == nil { + return true, nil + } + + x, ok := val.(bool) + if !ok { + return false, fmt.Errorf("invalid ON expression %s (value of type %T)", val, val) + } + + if !x { + return true, nil + } + + match = true + // rigth, left -> left, right + return f(ids, append(append([]interface{}(nil), row[right:]...), row[:right]...)) + }) + } + return g(nil, append([]plan{r.rsets[n-1]}, r.rsets[:n-1]...), 0) +} + +func (r *fullJoinDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error { + b3 := b.TreeNew(func(a, b interface{}) int { + x := a.(int64) + y := b.(int64) + if x < y { + return -1 + } + + if x == y { + return 0 + } + + return 1 + }) + m := map[interface{}]interface{}{} + ids := map[string]interface{}{} + var g func([]interface{}, []plan, int) error + var match bool + var rid int64 + firstR := true + g = func(prefix []interface{}, rsets []plan, x int) (err error) { + return rsets[0].do(ctx, func(id interface{}, in []interface{}) (bool, error) { + ids[r.names[x]] = id + row := append(prefix, in...) + if len(rsets) > 1 { + if len(rsets) == 2 { + match = false + rid = 0 + } + if err = g(row, rsets[1:], x+1); err != nil { + return false, err + } + + if len(rsets) == 2 { + firstR = false + } + if len(rsets) != 2 || match { + return true, nil + } + + ids[r.names[x+1]] = nil + return f(ids, append(row, make([]interface{}, r.right)...)) + } + + rid++ + if firstR { + b3.Set(rid, in) + } + for i, fld := range r.fields { + if fld != "" { + m[fld] = row[i] + } + } + + val, err := r.on.eval(ctx, m) + if err != nil { + return false, err + } + + if val == nil { + return true, nil + } + + x, ok := val.(bool) + if !ok { + return false, fmt.Errorf("invalid ON expression %s (value of type %T)", val, val) + } + + if !x { + return true, nil + } + + match = true + b3.Delete(rid) + return f(ids, row) + }) + } + if err := g(nil, r.rsets, 0); err != nil { + return err + } + + it, err := b3.SeekFirst() + if err != nil { + return noEOF(err) + } + + pref := make([]interface{}, len(r.fields)-r.right) + for { + _, v, err := it.Next() + if err != nil { + return noEOF(err) + } + + more, err := f(nil, append(pref, v.([]interface{})...)) + if err != nil || !more { + return err + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/ql.ebnf b/Godeps/_workspace/src/github.com/cznic/ql/ql.ebnf new file mode 100644 index 000000000..f8b418d8c --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/ql.ebnf @@ -0,0 +1,225 @@ +andand = "&&" . +andnot = "&^" . +ascii_letter = "a" … "z" | "A" … "Z" . +big_u_value = "\\" "U" hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit . +byte_value = octal_byte_value | hex_byte_value . +decimal_digit = "0" … "9" . +decimal_lit = ( "1" … "9" ) { decimal_digit } . +decimals = decimal_digit { decimal_digit } . +eq = "==" . +escaped_char = "\\" ( + "a" + | "b" + | "f" + | "n" + | "r" + | "t" + | "v" + | "\\" + | "'" + | "\"" + ) . +exponent = ( "e" | "E" ) [ "+" | "-" ] decimals . +float_lit = decimals "." [ decimals ] [ exponent ] + | decimals exponent + | "." decimals [ exponent ] . +ge = ">=" . +hex_byte_value = "\\" "x" hex_digit hex_digit . +hex_digit = "0" … "9" + | "A" … "F" + | "a" … "f" . +hex_lit = "0" ( "x" | "X" ) hex_digit { hex_digit } . +identifier = letter { letter | decimal_digit } . +imaginary_lit = ( decimals | float_lit ) "i" . +int_lit = decimal_lit + | octal_lit + | hex_lit . +interpreted_string_lit = "\"" { unicode_value | byte_value } "\"" . +le = "<=" . +letter = ascii_letter | "_" . +little_u_value = "\\" "u" hex_digit hex_digit hex_digit hex_digit . +lsh = "<<" . +neq = "!=" . +newline = . +octal_byte_value = "\\" octal_digit octal_digit octal_digit . +octal_digit = "0" … "7" . +octal_lit = "0" { octal_digit } . +oror = "||" . +ql_parameter = ( "?" | "$" ) "1" … "9" { "0" … "9" } . +raw_string_lit = "`" { unicode_char | newline } "`" . +rsh = ">>" . +rune_lit = "'" ( unicode_value | byte_value ) "'" . +string_lit = raw_string_lit | interpreted_string_lit . +unicode_char = . +unicode_value = unicode_char + | little_u_value + | big_u_value + | escaped_char . + +AlterTableStmt = "ALTER" "TABLE" TableName ( + "ADD" ColumnDef + | "DROP" "COLUMN" ColumnName + ) . +Assignment = ColumnName "=" Expression . +AssignmentList = Assignment { "," Assignment } [ "," ] . +BeginTransactionStmt = "BEGIN" "TRANSACTION" . +Call = "(" [ "*" | ExpressionList ] ")" . +ColumnDef = ColumnName Type [ + "NOT" "NULL" + | Expression + ] [ "DEFAULT" Expression ] . +ColumnName = identifier . +ColumnNameList = ColumnName { "," ColumnName } [ "," ] . +CommitStmt = "COMMIT" . +Conversion = Type "(" Expression ")" . +CreateIndexStmt = "CREATE" [ "UNIQUE" ] "INDEX" [ + "IF" "NOT" "EXISTS" + ] IndexName "ON" TableName "(" ExpressionList ")" . +CreateTableStmt = "CREATE" "TABLE" [ + "IF" "NOT" "EXISTS" + ] TableName "(" ColumnDef { "," ColumnDef } [ "," ] ")" . +DeleteFromStmt = "DELETE" "FROM" TableName [ WhereClause ] . +DropIndexStmt = "DROP" "INDEX" [ "IF" "EXISTS" ] IndexName . +DropTableStmt = "DROP" "TABLE" [ "IF" "EXISTS" ] TableName . +EmptyStmt = . +ExplainStmt = "EXPLAIN" Statement . +Expression = Term { + ( oror | "OR" ) Term + } . +ExpressionList = Expression { "," Expression } [ "," ] . +Factor = PrimaryFactor { + ( + ge + | ">" + | le + | "<" + | neq + | eq + | "LIKE" + ) PrimaryFactor + } [ Predicate ] . +Field = Expression [ "AS" identifier ] . +FieldList = Field { "," Field } [ "," ] . +GroupByClause = "GROUP BY" ColumnNameList . +Index = "[" Expression "]" . +IndexName = identifier . +InsertIntoStmt = "INSERT" "INTO" TableName [ + "(" ColumnNameList ")" + ] ( Values | SelectStmt ) . +JoinClause = ( + "LEFT" + | "RIGHT" + | "FULL" + ) [ "OUTER" ] "JOIN" RecordSet "ON" Expression . +Limit = "Limit" Expression . +Literal = "FALSE" + | "NULL" + | "TRUE" + | float_lit + | imaginary_lit + | int_lit + | rune_lit + | string_lit + | ql_parameter . +Offset = "OFFSET" Expression . +Operand = Literal + | QualifiedIdent + | "(" Expression ")" . +OrderBy = "ORDER" "BY" ExpressionList [ "ASC" | "DESC" ] . +Predicate = ( + [ "NOT" ] ( + "IN" "(" ExpressionList ")" + | "IN" "(" SelectStmt [ ";" ] ")" + | "BETWEEN" PrimaryFactor "AND" PrimaryFactor + ) + | "IS" [ "NOT" ] "NULL" + ) . +PrimaryExpression = Operand + | Conversion + | PrimaryExpression Index + | PrimaryExpression Slice + | PrimaryExpression Call . +PrimaryFactor = PrimaryTerm { + ( + "^" + | "|" + | "-" + | "+" + ) PrimaryTerm + } . +PrimaryTerm = UnaryExpr { + ( + andnot + | "&" + | lsh + | rsh + | "%" + | "/" + | "*" + ) UnaryExpr + } . +QualifiedIdent = identifier [ "." identifier ] . +RecordSet = ( + TableName + | "(" SelectStmt [ ";" ] ")" + ) [ "AS" identifier ] . +RecordSetList = RecordSet { "," RecordSet } [ "," ] . +RollbackStmt = "ROLLBACK" . +SelectStmt = "SELECT" [ "DISTINCT" ] ( "*" | FieldList ) "FROM" RecordSetList [ JoinClause ] [ WhereClause ] [ GroupByClause ] [ OrderBy ] [ Limit ] [ Offset ] . +Slice = "[" [ Expression ] ":" [ Expression ] "]" . +Statement = EmptyStmt + | AlterTableStmt + | BeginTransactionStmt + | CommitStmt + | CreateIndexStmt + | CreateTableStmt + | DeleteFromStmt + | DropIndexStmt + | DropTableStmt + | InsertIntoStmt + | RollbackStmt + | SelectStmt + | TruncateTableStmt + | UpdateStmt + | ExplainStmt . +StatementList = Statement { ";" Statement } . +TableName = identifier . +Term = Factor { + ( andand | "AND" ) Factor + } . +TruncateTableStmt = "TRUNCATE" "TABLE" TableName . +Type = "bigint" + | "bigrat" + | "blob" + | "bool" + | "byte" + | "complex128" + | "complex64" + | "duration" + | "float" + | "float32" + | "float64" + | "int" + | "int16" + | "int32" + | "int64" + | "int8" + | "rune" + | "string" + | "time" + | "uint" + | "uint16" + | "uint32" + | "uint64" + | "uint8" . +UnaryExpr = [ + "^" + | "!" + | "-" + | "+" + ] PrimaryExpression . +UpdateStmt = "UPDATE" TableName [ "SET" ] AssignmentList [ WhereClause ] . +Values = "VALUES" "(" ExpressionList ")" { + "," "(" ExpressionList ")" + } [ "," ] . +WhereClause = "WHERE" Expression . diff --git a/Godeps/_workspace/src/github.com/cznic/ql/ql.go b/Godeps/_workspace/src/github.com/cznic/ql/ql.go new file mode 100644 index 000000000..b777a158c --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/ql.go @@ -0,0 +1,1707 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//LATER profile mem +//LATER profile cpu +//LATER coverage + +package ql + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "strconv" + "strings" + "sync" + "time" + + "github.com/cznic/strutil" +) + +const ( + crossJoin = iota + leftJoin + rightJoin + fullJoin +) + +// NOTE: all rset implementations must be safe for concurrent use by multiple +// goroutines. If the do method requires any execution domain local data, they +// must be held out of the implementing instance. +var ( + _ rset = (*distinctRset)(nil) + _ rset = (*groupByRset)(nil) + _ rset = (*joinRset)(nil) + _ rset = (*limitRset)(nil) + _ rset = (*offsetRset)(nil) + _ rset = (*orderByRset)(nil) + _ rset = (*selectRset)(nil) + _ rset = (*selectStmt)(nil) + _ rset = (*tableRset)(nil) + _ rset = (*whereRset)(nil) + + isTesting bool // enables test hook: select from an index +) + +type rset interface { + plan(ctx *execCtx) (plan, error) +} + +type recordset struct { + ctx *execCtx + plan + tx *TCtx +} + +func (r recordset) fieldNames() []interface{} { + f := r.plan.fieldNames() + a := make([]interface{}, len(f)) + for i, v := range f { + a[i] = v + } + return a +} + +// Do implements Recordset. +func (r recordset) Do(names bool, f func(data []interface{}) (bool, error)) error { + if names { + if more, err := f(r.fieldNames()); err != nil || !more { + return err + } + } + return r.ctx.db.do(r, f) +} + +// Fields implements Recordset. +func (r recordset) Fields() (names []string, err error) { + return r.plan.fieldNames(), nil +} + +// FirstRow implements Recordset. +func (r recordset) FirstRow() (row []interface{}, err error) { + rows, err := r.Rows(1, 0) + if err != nil { + return nil, err + } + + if len(rows) != 0 { + return rows[0], nil + } + + return nil, nil +} + +// Rows implements Recordset. +func (r recordset) Rows(limit, offset int) ([][]interface{}, error) { + var rows [][]interface{} + if err := r.Do(false, func(row []interface{}) (bool, error) { + if offset > 0 { + offset-- + return true, nil + } + + switch { + case limit < 0: + rows = append(rows, row) + return true, nil + case limit == 0: + return false, nil + default: // limit > 0 + rows = append(rows, row) + limit-- + return limit > 0, nil + } + }); err != nil { + return nil, err + } + + return rows, nil +} + +// List represents a group of compiled statements. +type List struct { + l []stmt + params int +} + +// String implements fmt.Stringer +func (l List) String() string { + var b bytes.Buffer + f := strutil.IndentFormatter(&b, "\t") + for _, s := range l.l { + switch s.(type) { + case beginTransactionStmt: + f.Format("%s\n%i", s) + case commitStmt, rollbackStmt: + f.Format("%u%s\n", s) + default: + f.Format("%s\n", s) + } + } + return b.String() +} + +// IsExplainStmt reports whether l is a single EXPLAIN statment or a single EXPLAIN +// statment enclosed in a transaction. +func (l List) IsExplainStmt() bool { + switch len(l.l) { + case 1: + _, ok := l.l[0].(*explainStmt) + return ok + case 3: + if _, ok := l.l[0].(beginTransactionStmt); !ok { + return false + } + if _, ok := l.l[1].(*explainStmt); !ok { + return false + } + _, ok := l.l[2].(commitStmt) + return ok + default: + return false + } +} + +type groupByRset struct { + colNames []string + src plan +} + +func (r *groupByRset) plan(ctx *execCtx) (plan, error) { + fields := r.src.fieldNames() + for _, v := range r.colNames { + found := false + for _, v2 := range fields { + if v == v2 { + found = true + break + } + } + if !found { + return nil, fmt.Errorf("unknown field %s", v) + } + } + return &groupByDefaultPlan{colNames: r.colNames, src: r.src, fields: fields}, nil +} + +// TCtx represents transaction context. It enables to execute multiple +// statement lists in the same context. The same context guarantees the state +// of the DB cannot change in between the separated executions. +// +// LastInsertID +// +// LastInsertID is updated by INSERT INTO statements. The value considers +// performed ROLLBACK statements, if any, even though roll backed IDs are not +// reused. QL clients should treat the field as read only. +// +// RowsAffected +// +// RowsAffected is updated by INSERT INTO, DELETE FROM and UPDATE statements. +// The value does not (yet) consider any ROLLBACK statements involved. QL +// clients should treat the field as read only. +type TCtx struct { + LastInsertID int64 + RowsAffected int64 +} + +// NewRWCtx returns a new read/write transaction context. NewRWCtx is safe for +// concurrent use by multiple goroutines, every one of them will get a new, +// unique conext. +func NewRWCtx() *TCtx { return &TCtx{} } + +// Recordset is a result of a select statment. It can call a user function for +// every row (record) in the set using the Do method. +// +// Recordsets can be safely reused. Evaluation of the rows is performed lazily. +// Every invocation of Do will see the current, potentially actualized data. +// +// Do +// +// Do will call f for every row (record) in the Recordset. +// +// If f returns more == false or err != nil then f will not be called for any +// remaining rows in the set and the err value is returned from Do. +// +// If names == true then f is firstly called with a virtual row +// consisting of field (column) names of the RecordSet. +// +// Do is executed in a read only context and performs a RLock of the +// database. +// +// Do is safe for concurrent use by multiple goroutines. +// +// Fields +// +// Fields return a slice of field names of the recordset. The result is computed +// without actually computing the recordset rows. +// +// FirstRow +// +// FirstRow will return the first row of the RecordSet or an error, if any. If +// the Recordset has no rows the result is (nil, nil). +// +// Rows +// +// Rows will return rows in Recordset or an error, if any. The semantics of +// limit and offset are the same as of the LIMIT and OFFSET clauses of the +// SELECT statement. To get all rows pass limit < 0. If there are no rows to +// return the result is (nil, nil). +type Recordset interface { + Do(names bool, f func(data []interface{}) (more bool, err error)) error + Fields() (names []string, err error) + FirstRow() (row []interface{}, err error) + Rows(limit, offset int) (rows [][]interface{}, err error) +} + +type assignment struct { + colName string + expr expression +} + +func (a *assignment) String() string { + return fmt.Sprintf("%s=%s", a.colName, a.expr) +} + +type distinctRset struct { + src plan +} + +func (r *distinctRset) plan(ctx *execCtx) (plan, error) { + return &distinctDefaultPlan{src: r.src, fields: r.src.fieldNames()}, nil +} + +type orderByRset struct { + asc bool + by []expression + src plan +} + +func (r *orderByRset) String() string { + a := make([]string, len(r.by)) + for i, v := range r.by { + a[i] = v.String() + } + s := strings.Join(a, ", ") + if !r.asc { + s += " DESC" + } + return s +} + +func (r *orderByRset) plan(ctx *execCtx) (plan, error) { + if _, ok := r.src.(*nullPlan); ok { + return r.src, nil + } + + var by []expression + fields := r.src.fieldNames() + for _, e := range r.by { + cols := mentionedColumns(e) + for k := range cols { + found := false + for _, v := range fields { + if k == v { + found = true + break + } + } + if !found { + return nil, fmt.Errorf("unknown field %s", k) + } + } + if len(cols) == 0 { + v, err := e.eval(ctx, nil) + if err != nil { + by = append(by, e) + continue + } + + if isConstValue(v) != nil { + continue + } + } + + by = append(by, e) + } + return &orderByDefaultPlan{asc: r.asc, by: by, src: r.src, fields: fields}, nil +} + +type whereRset struct { + expr expression + src plan +} + +func (r *whereRset) planBinOp(x *binaryOperation) (plan, error) { + p := r.src + ok, cn := isColumnExpression(x.l) + if ok && cn == "id()" { + if v := isConstValue(x.r); v != nil { + v, err := typeCheck1(v, idCol) + if err != nil { + return nil, err + } + + rv := v.(int64) + switch { + case p.hasID(): + switch x.op { + case '<': + if rv <= 1 { + return &nullPlan{p.fieldNames()}, nil + } + case '>': + if rv <= 0 { + return p, nil + } + case ge: + if rv >= 1 { + return p, nil + } + case neq: + if rv <= 0 { + return p, nil + } + case eq: + if rv <= 0 { + return &nullPlan{p.fieldNames()}, nil + } + case le: + if rv <= 0 { + return &nullPlan{p.fieldNames()}, nil + } + } + } + } + } + + var err error + var p2 plan + var is []string + switch x.op { + case eq, ge, '>', le, '<', neq: + if p2, is, err = p.filter(x); err != nil { + return nil, err + } + + if p2 != nil { + return p2, nil + } + case andand: + var in []expression + var f func(expression) + f = func(e expression) { + b, ok := e.(*binaryOperation) + if !ok || b.op != andand { + in = append(in, e) + return + } + + f(b.l) + f(b.r) + } + f(x) + out := []expression{} + p := r.src + isNewPlan := false + for _, e := range in { + p2, is2, err := p.filter(e) + if err != nil { + return nil, err + } + + if p2 == nil { + is = append(is, is2...) + out = append(out, e) + continue + } + + p = p2 + isNewPlan = true + } + + if !isNewPlan { + break + } + + if len(out) == 0 { + return p, nil + } + + for len(out) > 1 { + n := len(out) + e, err := newBinaryOperation(andand, out[n-2], out[n-1]) + if err != nil { + return nil, err + } + + out = out[:n-1] + out[n-2] = e + } + + return &filterDefaultPlan{p, out[0], is}, nil + } + + return &filterDefaultPlan{p, x, is}, nil +} + +func (r *whereRset) planIdent(x *ident) (plan, error) { + p := r.src + p2, is, err := p.filter(x) + if err != nil { + return nil, err + } + + if p2 != nil { + return p2, nil + } + + return &filterDefaultPlan{p, x, is}, nil +} + +func (r *whereRset) planIsNull(x *isNull) (plan, error) { + p := r.src + ok, cn := isColumnExpression(x.expr) + if !ok { + return &filterDefaultPlan{p, x, nil}, nil + } + + if cn == "id()" { + switch { + case p.hasID(): + switch { + case x.not: // IS NOT NULL + return p, nil + default: // IS NULL + return &nullPlan{p.fieldNames()}, nil + } + default: + switch { + case x.not: // IS NOT NULL + return &nullPlan{p.fieldNames()}, nil + default: // IS NULL + return p, nil + } + } + } + + p2, is, err := p.filter(x) + if err != nil { + return nil, err + } + + if p2 != nil { + return p2, nil + } + + return &filterDefaultPlan{p, x, is}, nil +} + +func (r *whereRset) planUnaryOp(x *unaryOperation) (plan, error) { + p := r.src + p2, is, err := p.filter(x) + if err != nil { + return nil, err + } + + if p2 != nil { + return p2, nil + } + + return &filterDefaultPlan{p, x, is}, nil +} + +func (r *whereRset) plan(ctx *execCtx) (plan, error) { + expr, err := r.expr.clone(ctx.arg) + if err != nil { + return nil, err + } + + switch x := expr.(type) { + case *binaryOperation: + return r.planBinOp(x) + case *ident: + return r.planIdent(x) + case *isNull: + return r.planIsNull(x) + case *pIn: + //TODO optimize + //TODO show plan + case *pLike: + //TODO optimize + case *unaryOperation: + return r.planUnaryOp(x) + } + + return &filterDefaultPlan{r.src, expr, nil}, nil +} + +type offsetRset struct { + expr expression + src plan +} + +func (r *offsetRset) plan(ctx *execCtx) (plan, error) { + return &offsetDefaultPlan{expr: r.expr, src: r.src, fields: r.src.fieldNames()}, nil +} + +type limitRset struct { + expr expression + src plan +} + +func (r *limitRset) plan(ctx *execCtx) (plan, error) { + return &limitDefaultPlan{expr: r.expr, src: r.src, fields: r.src.fieldNames()}, nil +} + +type selectRset struct { + flds []*fld + src plan +} + +func (r *selectRset) plan(ctx *execCtx) (plan, error) { + var flds2 []*fld + if len(r.flds) != 0 { + m := map[string]struct{}{} + for _, v := range r.flds { + mentionedColumns0(v.expr, true, true, m) + } + for _, v := range r.src.fieldNames() { + delete(m, v) + } + for k := range m { + return nil, fmt.Errorf("unknown field %s", k) + } + + flds2 = append(flds2, r.flds...) + } + + if x, ok := r.src.(*groupByDefaultPlan); ok { + if len(r.flds) == 0 { + fields := x.fieldNames() + flds := make([]*fld, len(fields)) + for i, v := range fields { + flds[i] = &fld{&ident{v}, v} + } + return &selectFieldsGroupPlan{flds: flds, src: x, fields: fields}, nil + } + + p := &selectFieldsGroupPlan{flds: flds2, src: x} + for _, v := range r.flds { + p.fields = append(p.fields, v.name) + } + return p, nil + } + + if len(r.flds) == 0 { + return r.src, nil + } + + f0 := r.src.fieldNames() + if len(f0) == len(flds2) { + match := true + for i, v := range flds2 { + if x, ok := v.expr.(*ident); ok && x.s == f0[i] && v.name == f0[i] { + continue + } + + match = false + break + } + + if match { + return r.src, nil + } + } + + src := r.src + if x, ok := src.(*tableDefaultPlan); ok { + isconst := true + for _, v := range flds2 { + if isConstValue(v.expr) == nil { + isconst = false + break + } + } + if isconst { // #250 + src = &tableNilPlan{x.t} + } + } + + p := &selectFieldsDefaultPlan{flds: flds2, src: src} + for _, v := range r.flds { + p.fields = append(p.fields, v.name) + } + return p, nil +} + +type tableRset string + +func (r tableRset) plan(ctx *execCtx) (plan, error) { + switch r { + case "__Table": + return &sysTableDefaultPlan{}, nil + case "__Column": + return &sysColumnDefaultPlan{}, nil + case "__Index": + return &sysIndexDefaultPlan{}, nil + } + + t, ok := ctx.db.root.tables[string(r)] + if !ok && isTesting { + if _, x0 := ctx.db.root.findIndexByName(string(r)); x0 != nil { + return &selectIndexDefaultPlan{nm: string(r), x: x0}, nil + } + } + + if !ok { + return nil, fmt.Errorf("table %s does not exist", r) + } + + rs := &tableDefaultPlan{t: t} + for _, col := range t.cols { + rs.fields = append(rs.fields, col.name) + } + return rs, nil +} + +func findFldIndex(fields []*fld, name string) int { + for i, f := range fields { + if f.name == name { + return i + } + } + + return -1 +} + +func findFld(fields []*fld, name string) (f *fld) { + for _, f = range fields { + if f.name == name { + return + } + } + + return nil +} + +type col struct { + index int + name string + typ int + constraint *constraint + dflt expression +} + +var idCol = &col{name: "id()", typ: qInt64} + +func findCol(cols []*col, name string) (c *col) { + for _, c = range cols { + if c.name == name { + return + } + } + + return nil +} + +func (f *col) clone() *col { + var r col + r = *f + r.constraint = f.constraint.clone() + if f.dflt != nil { + r.dflt, _ = r.dflt.clone(nil) + } + return &r +} + +func (f *col) typeCheck(x interface{}) (ok bool) { //NTYPE + switch x.(type) { + case nil: + return true + case bool: + return f.typ == qBool + case complex64: + return f.typ == qComplex64 + case complex128: + return f.typ == qComplex128 + case float32: + return f.typ == qFloat32 + case float64: + return f.typ == qFloat64 + case int8: + return f.typ == qInt8 + case int16: + return f.typ == qInt16 + case int32: + return f.typ == qInt32 + case int64: + return f.typ == qInt64 + case string: + return f.typ == qString + case uint8: + return f.typ == qUint8 + case uint16: + return f.typ == qUint16 + case uint32: + return f.typ == qUint32 + case uint64: + return f.typ == qUint64 + case []byte: + return f.typ == qBlob + case *big.Int: + return f.typ == qBigInt + case *big.Rat: + return f.typ == qBigRat + case time.Time: + return f.typ == qTime + case time.Duration: + return f.typ == qDuration + case chunk: + return true // was checked earlier + } + return +} + +func cols2meta(f []*col) (s string) { + a := []string{} + for _, f := range f { + a = append(a, string(f.typ)+f.name) + } + return strings.Join(a, "|") +} + +// DB represent the database capable of executing QL statements. +type DB struct { + cc *TCtx // Current transaction context + isMem bool + mu sync.Mutex + root *root + rw bool // DB FSM + rwmu sync.RWMutex + store storage + tnl int // Transaction nesting level + exprCache map[string]expression + exprCacheMu sync.Mutex + hasIndex2 int // 0: nope, 1: in progress, 2: yes. +} + +var selIndex2Expr = MustCompile("select Expr from __Index2_Expr where Index2_ID == $1") + +func newDB(store storage) (db *DB, err error) { + db0 := &DB{ + exprCache: map[string]expression{}, + store: store, + } + if db0.root, err = newRoot(store); err != nil { + return + } + + ctx := &execCtx{db: db0} + for _, t := range db0.root.tables { + if err := t.constraintsAndDefaults(ctx); err != nil { + return nil, err + } + } + + if !db0.hasAllIndex2() { + return db0, nil + } + + db0.hasIndex2 = 2 + rss, _, err := db0.Run(nil, "select id(), TableName, IndexName, IsUnique, Root from __Index2 where !IsSimple") + if err != nil { + return nil, err + } + + rows, err := rss[0].Rows(-1, 0) + if err != nil { + return nil, err + } + + for _, row := range rows { + defer func() { + if e := recover(); e != nil { + err = fmt.Errorf("error loading DB indices: %v", e) + } + }() + + id := row[0].(int64) + tn := row[1].(string) + xn := row[2].(string) + unique := row[3].(bool) + xroot := row[4].(int64) + + t := db0.root.tables[tn] + if t == nil { + return nil, fmt.Errorf("DB index refers to nonexistent table: %s", tn) + } + + x, err := store.OpenIndex(unique, xroot) + if err != nil { + return nil, err + } + + if v := t.indices2[xn]; v != nil { + return nil, fmt.Errorf("duplicate DB index: %s", xn) + } + + ix := &index2{ + unique: unique, + x: x, + xroot: xroot, + } + + rss, _, err := db0.Execute(nil, selIndex2Expr, id) + if err != nil { + return nil, err + } + + rows, err := rss[0].Rows(-1, 0) + if err != nil { + return nil, err + } + + if len(rows) == 0 { + return nil, fmt.Errorf("index has no expression: %s", xn) + } + + var sources []string + var list []expression + for _, row := range rows { + src, ok := row[0].(string) + if !ok { + return nil, fmt.Errorf("index %s: expression of type %T", xn, row[0]) + } + + expr, err := db0.str2expr(src) + if err != nil { + return nil, fmt.Errorf("index %s: expression error: %v", xn, err) + } + + sources = append(sources, src) + list = append(list, expr) + } + + ix.sources = sources + ix.exprList = list + if t.indices2 == nil { + t.indices2 = map[string]*index2{} + } + t.indices2[xn] = ix + } + return db0, nil +} + +func (db *DB) deleteIndex2ByIndexName(nm string) error { + for _, s := range deleteIndex2ByIndexName.l { + if _, err := s.exec(&execCtx{db: db, arg: []interface{}{nm}}); err != nil { + return err + } + } + return nil +} + +func (db *DB) deleteIndex2ByTableName(nm string) error { + for _, s := range deleteIndex2ByTableName.l { + if _, err := s.exec(&execCtx{db: db, arg: []interface{}{nm}}); err != nil { + return err + } + } + return nil +} + +func (db *DB) createIndex2() error { + if db.hasIndex2 != 0 { + return nil + } + + db.hasIndex2 = 1 + ctx := execCtx{db: db} + for _, s := range createIndex2.l { + if _, err := s.exec(&ctx); err != nil { + db.hasIndex2 = 0 + return err + } + } + + for t := db.root.thead; t != nil; t = t.tnext { + for i, index := range t.indices { + if index == nil { + continue + } + + expr := "id()" + if i != 0 { + expr = t.cols[i-1].name + } + + if err := db.insertIndex2(t.name, index.name, []string{expr}, index.unique, true, index.xroot); err != nil { + db.hasIndex2 = 0 + return err + } + } + } + + db.hasIndex2 = 2 + return nil +} + +func (db *DB) insertIndex2(tableName, indexName string, expr []string, unique, isSimple bool, h int64) error { + ctx := execCtx{db: db} + ctx.arg = []interface{}{ + tableName, + indexName, + unique, + isSimple, + h, + } + if _, err := insertIndex2.l[0].exec(&ctx); err != nil { + return err + } + + id := db.root.lastInsertID + for _, e := range expr { + ctx.arg = []interface{}{id, e} + if _, err := insertIndex2Expr.l[0].exec(&ctx); err != nil { + return err + } + } + return nil +} + +func (db *DB) hasAllIndex2() bool { + t := db.root.tables + if _, ok := t["__Index2"]; !ok { + return false + } + + _, ok := t["__Index2_Expr"] + return ok +} + +func (db *DB) str2expr(expr string) (expression, error) { + db.exprCacheMu.Lock() + e := db.exprCache[expr] + db.exprCacheMu.Unlock() + if e != nil { + return e, nil + } + + e, err := compileExpr(expr) + if err != nil { + return nil, err + } + + db.exprCacheMu.Lock() + for k := range db.exprCache { + if len(db.exprCache) < 1000 { + break + } + + delete(db.exprCache, k) + } + db.exprCache[expr] = e + db.exprCacheMu.Unlock() + return e, nil +} + +// Name returns the name of the DB. +func (db *DB) Name() string { return db.store.Name() } + +// Run compiles and executes a statement list. It returns, if applicable, a +// RecordSet slice and/or an index and error. +// +// For more details please see DB.Execute +// +// Run is safe for concurrent use by multiple goroutines. +func (db *DB) Run(ctx *TCtx, ql string, arg ...interface{}) (rs []Recordset, index int, err error) { + l, err := Compile(ql) + if err != nil { + return nil, -1, err + } + + return db.Execute(ctx, l, arg...) +} + +func (db *DB) run(ctx *TCtx, ql string, arg ...interface{}) (rs []Recordset, index int, err error) { + l, err := compile(ql) + if err != nil { + return nil, -1, err + } + + return db.Execute(ctx, l, arg...) +} + +// Compile parses the ql statements from src and returns a compiled list for +// DB.Execute or an error if any. +// +// Compile is safe for concurrent use by multiple goroutines. +func Compile(src string) (List, error) { + l := newLexer(src) + if yyParse(l) != 0 { + return List{}, l.errs[0] + } + + return List{l.list, l.params}, nil +} + +func compileExpr(src string) (expression, error) { + l := newLexer(src) + l.inj = parseExpression + if yyParse(l) != 0 { + return nil, l.errs[0] + } + + return l.expr, nil +} + +func compile(src string) (List, error) { + l := newLexer(src) + l.root = true + if yyParse(l) != 0 { + return List{}, l.errs[0] + } + + return List{l.list, l.params}, nil +} + +// MustCompile is like Compile but panics if the ql statements in src cannot be +// compiled. It simplifies safe initialization of global variables holding +// compiled statement lists for DB.Execute. +// +// MustCompile is safe for concurrent use by multiple goroutines. +func MustCompile(src string) List { + list, err := Compile(src) + if err != nil { + panic("ql: Compile(" + strconv.Quote(src) + "): " + err.Error()) // panic ok here + } + + return list +} + +func mustCompile(src string) List { + list, err := compile(src) + if err != nil { + panic("ql: compile(" + strconv.Quote(src) + "): " + err.Error()) // panic ok here + } + + return list +} + +// Execute executes statements in a list while substituting QL paramaters from +// arg. +// +// The resulting []Recordset corresponds to the SELECT FROM statements in the +// list. +// +// If err != nil then index is the zero based index of the failed QL statement. +// Empty statements do not count. +// +// The FSM STT describing the relations between DB states, statements and the +// ctx parameter. +// +// +-----------+---------------------+------------------+------------------+------------------+ +// |\ Event | | | | | +// | \-------\ | BEGIN | | | Other | +// | State \| TRANSACTION | COMMIT | ROLLBACK | statement | +// +-----------+---------------------+------------------+------------------+------------------+ +// | RD | if PC == nil | return error | return error | DB.RLock | +// | | return error | | | Execute(1) | +// | CC == nil | | | | DB.RUnlock | +// | TNL == 0 | DB.Lock | | | | +// | | CC = PC | | | | +// | | TNL++ | | | | +// | | DB.BeginTransaction | | | | +// | | State = WR | | | | +// +-----------+---------------------+------------------+------------------+------------------+ +// | WR | if PC == nil | if PC != CC | if PC != CC | if PC == nil | +// | | return error | return error | return error | DB.Rlock | +// | CC != nil | | | | Execute(1) | +// | TNL != 0 | if PC != CC | DB.Commit | DB.Rollback | RUnlock | +// | | DB.Lock | TNL-- | TNL-- | else if PC != CC | +// | | CC = PC | if TNL == 0 | if TNL == 0 | return error | +// | | | CC = nil | CC = nil | else | +// | | TNL++ | State = RD | State = RD | Execute(2) | +// | | DB.BeginTransaction | DB.Unlock | DB.Unlock | | +// +-----------+---------------------+------------------+------------------+------------------+ +// CC: Curent transaction context +// PC: Passed transaction context +// TNL: Transaction nesting level +// +// Lock, Unlock, RLock, RUnlock semantics above are the same as in +// sync.RWMutex. +// +// (1): Statement list is executed outside of a transaction. Attempts to update +// the DB will fail, the execution context is read-only. Other statements with +// read only context will execute concurrently. If any statement fails, the +// execution of the statement list is aborted. +// +// Note that the RLock/RUnlock surrounds every single "other" statement when it +// is executed outside of a transaction. If read consistency is required by a +// list of more than one statement then an explicit BEGIN TRANSACTION / COMMIT +// or ROLLBACK wrapper must be provided. Otherwise the state of the DB may +// change in between executing any two out-of-transaction statements. +// +// (2): Statement list is executed inside an isolated transaction. Execution of +// statements can update the DB, the execution context is read-write. If any +// statement fails, the execution of the statement list is aborted and the DB +// is automatically rolled back to the TNL which was active before the start of +// execution of the statement list. +// +// Execute is safe for concurrent use by multiple goroutines, but one must +// consider the blocking issues as discussed above. +// +// ACID +// +// Atomicity: Transactions are atomic. Transactions can be nested. Commit or +// rollbacks work on the current transaction level. Transactions are made +// persistent only on the top level commit. Reads made from within an open +// transaction are dirty reads. +// +// Consistency: Transactions bring the DB from one structurally consistent +// state to other structurally consistent state. +// +// Isolation: Transactions are isolated. Isolation is implemented by +// serialization. +// +// Durability: Transactions are durable. A two phase commit protocol and a +// write ahead log is used. Database is recovered after a crash from the write +// ahead log automatically on open. +func (db *DB) Execute(ctx *TCtx, l List, arg ...interface{}) (rs []Recordset, index int, err error) { + // Sanitize args + for i, v := range arg { + switch x := v.(type) { + case nil, bool, complex64, complex128, float32, float64, string, + int8, int16, int32, int64, int, + uint8, uint16, uint32, uint64, uint, + *big.Int, *big.Rat, []byte, time.Duration, time.Time: + case big.Int: + arg[i] = &x + case big.Rat: + arg[i] = &x + default: + return nil, 0, fmt.Errorf("cannot use arg[%d] (type %T):unsupported type", i, v) + } + } + + tnl0 := db.tnl + if ctx != nil { + ctx.LastInsertID, ctx.RowsAffected = 0, 0 + } + + list := l.l + for _, s := range list { + r, err := db.run1(ctx, s, arg...) + if err != nil { + for db.tnl > tnl0 { + if _, e2 := db.run1(ctx, rollbackStmt{}); e2 != nil { + err = e2 + } + } + return rs, index, err + } + + if r != nil { + rs = append(rs, r) + } + } + return +} + +func (db *DB) run1(pc *TCtx, s stmt, arg ...interface{}) (rs Recordset, err error) { + db.mu.Lock() + switch db.rw { + case false: + switch s.(type) { + case beginTransactionStmt: + defer db.mu.Unlock() + if pc == nil { + return nil, errors.New("BEGIN TRANSACTION: cannot start a transaction in nil TransactionCtx") + } + + if err = db.store.BeginTransaction(); err != nil { + return + } + + db.beginTransaction() + db.rwmu.Lock() + db.cc = pc + db.tnl++ + db.rw = true + return + case commitStmt: + defer db.mu.Unlock() + return nil, errCommitNotInTransaction + case rollbackStmt: + defer db.mu.Unlock() + return nil, errRollbackNotInTransaction + default: + if s.isUpdating() { + db.mu.Unlock() + return nil, fmt.Errorf("attempt to update the DB outside of a transaction") + } + + db.rwmu.RLock() // can safely grab before Unlock + db.mu.Unlock() + defer db.rwmu.RUnlock() + return s.exec(&execCtx{db, arg}) // R/O tctx + } + default: // case true: + switch s.(type) { + case beginTransactionStmt: + defer db.mu.Unlock() + + if pc == nil { + return nil, errBeginTransNoCtx + } + + if pc != db.cc { + for db.rw == true { + db.mu.Unlock() // Transaction isolation + db.mu.Lock() + } + + db.rw = true + db.rwmu.Lock() + } + + if err = db.store.BeginTransaction(); err != nil { + return + } + + db.beginTransaction() + db.cc = pc + db.tnl++ + return + case commitStmt: + defer db.mu.Unlock() + if pc != db.cc { + return nil, fmt.Errorf("invalid passed transaction context") + } + + db.commit() + err = db.store.Commit() + db.tnl-- + if db.tnl != 0 { + return + } + + db.cc = nil + db.rw = false + db.rwmu.Unlock() + return + case rollbackStmt: + defer db.mu.Unlock() + defer func() { pc.LastInsertID = db.root.lastInsertID }() + if pc != db.cc { + return nil, fmt.Errorf("invalid passed transaction context") + } + + db.rollback() + err = db.store.Rollback() + db.tnl-- + if db.tnl != 0 { + return + } + + db.cc = nil + db.rw = false + db.rwmu.Unlock() + return + default: + if pc == nil { + if s.isUpdating() { + db.mu.Unlock() + return nil, fmt.Errorf("attempt to update the DB outside of a transaction") + } + + db.mu.Unlock() // must Unlock before RLock + db.rwmu.RLock() + defer db.rwmu.RUnlock() + return s.exec(&execCtx{db, arg}) + } + + defer db.mu.Unlock() + defer func() { pc.LastInsertID = db.root.lastInsertID }() + if pc != db.cc { + return nil, fmt.Errorf("invalid passed transaction context") + } + + return s.exec(&execCtx{db, arg}) + } + } +} + +// Flush ends the transaction collecting window, if applicable. IOW, if the DB +// is dirty, it schedules a 2PC (WAL + DB file) commit on the next outer most +// DB.Commit or performs it synchronously if there's currently no open +// transaction. +// +// The collecting window is an implementation detail and future versions of +// Flush may become a no operation while keeping the operation semantics. +func (db *DB) Flush() (err error) { + return nil +} + +// Close will close the DB. Successful Close is idempotent. +func (db *DB) Close() error { + db.mu.Lock() + defer db.mu.Unlock() + if db.store == nil { + return nil + } + + if db.tnl != 0 { + return fmt.Errorf("cannot close DB while open transaction exist") + } + + err := db.store.Close() + db.root, db.store = nil, nil + return err +} + +func (db *DB) do(r recordset, f func(data []interface{}) (bool, error)) (err error) { + db.mu.Lock() + switch db.rw { + case false: + db.rwmu.RLock() // can safely grab before Unlock + db.mu.Unlock() + defer db.rwmu.RUnlock() + default: // case true: + if r.tx == nil { + db.mu.Unlock() // must Unlock before RLock + db.rwmu.RLock() + defer db.rwmu.RUnlock() + break + } + + defer db.mu.Unlock() + if r.tx != db.cc { + return fmt.Errorf("invalid passed transaction context") + } + } + + return r.do(r.ctx, func(id interface{}, data []interface{}) (bool, error) { + if err = expand(data); err != nil { + return false, err + } + + return f(data) + }) +} + +func (db *DB) beginTransaction() { //TODO Rewrite, must use much smaller undo info! + root := *db.root + root.parent = db.root + root.tables = make(map[string]*table, len(db.root.tables)) + var tprev *table + for t := db.root.thead; t != nil; t = t.tnext { + t2 := t.clone() + root.tables[t2.name] = t2 + t2.tprev = tprev + switch { + case tprev == nil: + root.thead = t2 + default: + tprev.tnext = t2 + } + tprev = t2 + } + db.root = &root +} + +func (db *DB) rollback() { + db.root = db.root.parent +} + +func (db *DB) commit() { + db.root.parent = db.root.parent.parent +} + +// Type represents a QL type (bigint, int, string, ...) +type Type int + +// Values of ColumnInfo.Type. +const ( + BigInt Type = qBigInt + BigRat = qBigRat + Blob = qBlob + Bool = qBool + Complex128 = qComplex128 + Complex64 = qComplex64 + Duration = qDuration + Float32 = qFloat32 + Float64 = qFloat64 + Int16 = qInt16 + Int32 = qInt32 + Int64 = qInt64 + Int8 = qInt8 + String = qString + Time = qTime + Uint16 = qUint16 + Uint32 = qUint32 + Uint64 = qUint64 + Uint8 = qUint8 +) + +// String implements fmt.Stringer. +func (t Type) String() string { + return typeStr(int(t)) +} + +// ColumnInfo provides meta data describing a table column. +type ColumnInfo struct { + Name string // Column name. + Type Type // Column type (BigInt, BigRat, ...). + NotNull bool // Column cannot be NULL. + Constraint string // Constraint expression, if any. + Default string // Default expression, if any. +} + +// TableInfo provides meta data describing a DB table. +type TableInfo struct { + // Table name. + Name string + + // Table schema. Columns are listed in the order in which they appear + // in the schema. + Columns []ColumnInfo +} + +// IndexInfo provides meta data describing a DB index. It corresponds to the +// statement +// +// CREATE INDEX Name ON Table (Column); +type IndexInfo struct { + Name string // Index name + Table string // Table name. + Column string // Column name. + Unique bool // Wheter the index is unique. + ExpressionList []string // Index expression list. +} + +// DbInfo provides meta data describing a DB. +type DbInfo struct { + Name string // DB name. + Tables []TableInfo // Tables in the DB. + Indices []IndexInfo // Indices in the DB. +} + +func (db *DB) info() (r *DbInfo, err error) { + _, hasColumn2 := db.root.tables["__Column2"] + r = &DbInfo{Name: db.Name()} + for nm, t := range db.root.tables { + ti := TableInfo{Name: nm} + m := map[string]*ColumnInfo{} + if hasColumn2 { + rs, err := selectColumn2.l[0].exec(&execCtx{db: db, arg: []interface{}{nm}}) + if err != nil { + return nil, err + } + + if err := rs.(recordset).do( + &execCtx{db: db, arg: []interface{}{nm}}, + func(id interface{}, data []interface{}) (bool, error) { + ci := &ColumnInfo{NotNull: data[1].(bool), Constraint: data[2].(string), Default: data[3].(string)} + m[data[0].(string)] = ci + return true, nil + }, + ); err != nil { + return nil, err + } + } + for _, c := range t.cols { + ci := ColumnInfo{Name: c.name, Type: Type(c.typ)} + if c2 := m[c.name]; c2 != nil { + ci.NotNull = c2.NotNull + ci.Constraint = c2.Constraint + ci.Default = c2.Default + } + ti.Columns = append(ti.Columns, ci) + } + r.Tables = append(r.Tables, ti) + for i, x := range t.indices { + if x == nil { + continue + } + + var cn string + switch { + case i == 0: + cn = "id()" + default: + cn = t.cols0[i-1].name + } + r.Indices = append(r.Indices, IndexInfo{x.name, nm, cn, x.unique, []string{cn}}) + } + var a []string + for k := range t.indices2 { + a = append(a, k) + } + for _, k := range a { + x := t.indices2[k] + a = a[:0] + for _, e := range x.exprList { + a = append(a, e.String()) + } + r.Indices = append(r.Indices, IndexInfo{k, nm, "", x.unique, a}) + } + } + return +} + +// Info provides meta data describing a DB or an error if any. It locks the DB +// to obtain the result. +func (db *DB) Info() (r *DbInfo, err error) { + db.mu.Lock() + defer db.mu.Unlock() + return db.info() +} + +type constraint struct { + expr expression // If expr == nil: constraint is 'NOT NULL' +} + +func (c *constraint) clone() *constraint { + if c == nil { + return nil + } + + var e expression + if c.expr != nil { + e, _ = c.expr.clone(nil) + } + return &constraint{e} +} + +type joinRset struct { + sources []interface{} + typ int + on expression +} + +func (r *joinRset) String() string { + a := make([]string, len(r.sources)) + for i, pair0 := range r.sources { + pair := pair0.([]interface{}) + altName := pair[1].(string) + switch x := pair[0].(type) { + case string: // table name + switch { + case altName == "": + a[i] = x + default: + a[i] = fmt.Sprintf("%s AS %s", x, altName) + } + case *selectStmt: + switch { + case altName == "": + a[i] = fmt.Sprintf("(%s)", x) + default: + a[i] = fmt.Sprintf("(%s) AS %s", x, altName) + } + default: + panic("internal error 054") + } + } + n := len(a) + a2 := a[:n-1] + j := a[n-1] + var s string + switch r.typ { + case crossJoin: + return strings.Join(a, ", ") + case leftJoin: + s = strings.Join(a2, ",") + " LEFT" + case rightJoin: + s = strings.Join(a2, ",") + " RIGHT" + case fullJoin: + s = strings.Join(a2, ",") + " FULL" + } + s += " OUTER JOIN " + j + " ON " + r.on.String() + return s +} + +func (r *joinRset) plan(ctx *execCtx) (plan, error) { + rsets := make([]plan, len(r.sources)) + names := make([]string, len(r.sources)) + var err error + m := map[string]bool{} + var fields []string + for i, v := range r.sources { + pair := v.([]interface{}) + src := pair[0] + nm := pair[1].(string) + if s, ok := src.(string); ok { + src = tableRset(s) + if nm == "" { + nm = s + } + } + if m[nm] { + return nil, fmt.Errorf("%s: duplicate name %s", r.String(), nm) + } + + if nm != "" { + m[nm] = true + } + names[i] = nm + var q plan + switch x := src.(type) { + case rset: + if q, err = x.plan(ctx); err != nil { + return nil, err + } + case plan: + q = x + default: + panic("internal error 008") + } + + switch { + case len(r.sources) == 1: + fields = q.fieldNames() + default: + for _, f := range q.fieldNames() { + if strings.Contains(f, ".") { + return nil, fmt.Errorf("cannot join on recordset with already qualified field names (use the AS clause): %s", f) + } + + if f != "" && nm != "" { + f = fmt.Sprintf("%s.%s", nm, f) + } + if nm == "" { + f = "" + } + fields = append(fields, f) + } + } + rsets[i] = q + } + + if len(rsets) == 1 { + return rsets[0], nil + } + + right := len(rsets[len(rsets)-1].fieldNames()) + switch r.typ { + case crossJoin: + return &crossJoinDefaultPlan{rsets: rsets, names: names, fields: fields}, nil + case leftJoin: + return &leftJoinDefaultPlan{rsets: rsets, names: names, fields: fields, on: r.on, right: right}, nil + case rightJoin: + return &rightJoinDefaultPlan{leftJoinDefaultPlan{rsets: rsets, names: names, fields: fields, on: r.on, right: right}}, nil + case fullJoin: + return &fullJoinDefaultPlan{leftJoinDefaultPlan{rsets: rsets, names: names, fields: fields, on: r.on, right: right}}, nil + default: + panic("internal error 010") + } +} + +type fld struct { + expr expression + name string +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/ql.y b/Godeps/_workspace/src/github.com/cznic/ql/ql.y new file mode 100644 index 000000000..4655d713e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/ql.y @@ -0,0 +1,1687 @@ +%{ + +//TODO Put your favorite license here + +// yacc source generated by ebnf2y[1] +// at 2015-06-15 12:49:34.885562201 +0200 CEST +// +// $ ebnf2y -o ql.y -oe ql.ebnf -start StatementList -pkg ql -p _ +// +// CAUTION: If this file is a Go source file (*.go), it was generated +// automatically by '$ go tool yacc' from a *.y file - DO NOT EDIT in that case! +// +// [1]: http://github.com/cznic/ebnf2y + +package ql //TODO real package name + +//TODO required only be the demo _dump function +import ( + "bytes" + "fmt" + "strings" + + "github.com/cznic/strutil" +) + +%} + +%union { + item interface{} //TODO insert real field(s) +} + +%token _ANDAND +%token _ANDNOT +%token _EQ +%token _FLOAT_LIT +%token _GE +%token _IDENTIFIER +%token _IMAGINARY_LIT +%token _INT_LIT +%token _LE +%token _LSH +%token _NEQ +%token _OROR +%token _QL_PARAMETER +%token _RSH +%token _RUNE_LIT +%token _STRING_LIT + +%type /*TODO real type(s), if/where applicable */ + _ANDAND + _ANDNOT + _EQ + _FLOAT_LIT + _GE + _IDENTIFIER + _IMAGINARY_LIT + _INT_LIT + _LE + _LSH + _NEQ + _OROR + _QL_PARAMETER + _RSH + _RUNE_LIT + _STRING_LIT + +%token _ADD +%token _ALTER +%token _AND +%token _AS +%token _ASC +%token _BEGIN +%token _BETWEEN +%token _BIGINT +%token _BIGRAT +%token _BLOB +%token _BOOL +%token _BY +%token _BYTE +%token _COLUMN +%token _COMMIT +%token _COMPLEX128 +%token _COMPLEX64 +%token _CREATE +%token _DEFAULT +%token _DELETE +%token _DESC +%token _DISTINCT +%token _DROP +%token _DURATION +%token _EXISTS +%token _EXPLAIN +%token _FALSE +%token _FLOAT +%token _FLOAT32 +%token _FLOAT64 +%token _FROM +%token _FULL +%token _GROUPBY +%token _IF +%token _IN +%token _INDEX +%token _INSERT +%token _INT +%token _INT16 +%token _INT32 +%token _INT64 +%token _INT8 +%token _INTO +%token _IS +%token _JOIN +%token _LEFT +%token _LIKE +%token _LIMIT +%token _NOT +%token _NULL +%token _OFFSET +%token _ON +%token _OR +%token _ORDER +%token _OUTER +%token _RIGHT +%token _ROLLBACK +%token _RUNE +%token _SELECT +%token _SET +%token _STRING +%token _TABLE +%token _TIME +%token _TRANSACTION +%token _TRUE +%token _TRUNCATE +%token _UINT +%token _UINT16 +%token _UINT32 +%token _UINT64 +%token _UINT8 +%token _UNIQUE +%token _UPDATE +%token _VALUES +%token _WHERE + +%type /*TODO real type(s), if/where applicable */ + AlterTableStmt + AlterTableStmt1 + Assignment + AssignmentList + AssignmentList1 + AssignmentList2 + BeginTransactionStmt + Call + Call1 + Call11 + ColumnDef + ColumnDef1 + ColumnDef11 + ColumnDef2 + ColumnName + ColumnNameList + ColumnNameList1 + ColumnNameList2 + CommitStmt + Conversion + CreateIndexStmt + CreateIndexStmt1 + CreateIndexStmt2 + CreateTableStmt + CreateTableStmt1 + CreateTableStmt2 + CreateTableStmt3 + DeleteFromStmt + DeleteFromStmt1 + DropIndexStmt + DropIndexStmt1 + DropTableStmt + DropTableStmt1 + EmptyStmt + ExplainStmt + Expression + Expression1 + Expression11 + ExpressionList + ExpressionList1 + ExpressionList2 + Factor + Factor1 + Factor11 + Factor2 + Field + Field1 + FieldList + FieldList1 + FieldList2 + GroupByClause + Index + IndexName + InsertIntoStmt + InsertIntoStmt1 + InsertIntoStmt2 + JoinClause + JoinClause1 + JoinClause2 + Limit + Literal + Offset + Operand + OrderBy + OrderBy1 + OrderBy11 + Predicate + Predicate1 + Predicate11 + Predicate12 + Predicate121 + Predicate13 + PrimaryExpression + PrimaryFactor + PrimaryFactor1 + PrimaryFactor11 + PrimaryTerm + PrimaryTerm1 + PrimaryTerm11 + QualifiedIdent + QualifiedIdent1 + RecordSet + RecordSet1 + RecordSet11 + RecordSet2 + RecordSetList + RecordSetList1 + RecordSetList2 + RollbackStmt + SelectStmt + SelectStmt1 + SelectStmt2 + SelectStmt3 + SelectStmt4 + SelectStmt5 + SelectStmt6 + SelectStmt7 + SelectStmt8 + Slice + Slice1 + Slice2 + Start + Statement + StatementList + StatementList1 + TableName + Term + Term1 + Term11 + TruncateTableStmt + Type + UnaryExpr + UnaryExpr1 + UnaryExpr11 + UpdateStmt + UpdateStmt1 + UpdateStmt2 + Values + Values1 + Values2 + WhereClause + +/*TODO %left, %right, ... declarations */ + +%start Start + +%% + +AlterTableStmt: + _ALTER _TABLE TableName AlterTableStmt1 + { + $$ = []AlterTableStmt{"ALTER", "TABLE", $3, $4} //TODO 1 + } + +AlterTableStmt1: + _ADD ColumnDef + { + $$ = []AlterTableStmt1{"ADD", $2} //TODO 2 + } +| _DROP _COLUMN ColumnName + { + $$ = []AlterTableStmt1{"DROP", "COLUMN", $3} //TODO 3 + } + +Assignment: + ColumnName '=' Expression + { + $$ = []Assignment{$1, "=", $3} //TODO 4 + } + +AssignmentList: + Assignment AssignmentList1 AssignmentList2 + { + $$ = []AssignmentList{$1, $2, $3} //TODO 5 + } + +AssignmentList1: + /* EMPTY */ + { + $$ = []AssignmentList1(nil) //TODO 6 + } +| AssignmentList1 ',' Assignment + { + $$ = append($1.([]AssignmentList1), ",", $3) //TODO 7 + } + +AssignmentList2: + /* EMPTY */ + { + $$ = nil //TODO 8 + } +| ',' + { + $$ = "," //TODO 9 + } + +BeginTransactionStmt: + _BEGIN _TRANSACTION + { + $$ = []BeginTransactionStmt{"BEGIN", "TRANSACTION"} //TODO 10 + } + +Call: + '(' Call1 ')' + { + $$ = []Call{"(", $2, ")"} //TODO 11 + } + +Call1: + /* EMPTY */ + { + $$ = nil //TODO 12 + } +| Call11 + { + $$ = $1 //TODO 13 + } + +Call11: + '*' + { + $$ = "*" //TODO 14 + } +| ExpressionList + { + $$ = $1 //TODO 15 + } + +ColumnDef: + ColumnName Type ColumnDef1 ColumnDef2 + { + $$ = []ColumnDef{$1, $2, $3, $4} //TODO 16 + } + +ColumnDef1: + /* EMPTY */ + { + $$ = nil //TODO 17 + } +| ColumnDef11 + { + $$ = $1 //TODO 18 + } + +ColumnDef11: + _NOT _NULL + { + $$ = []ColumnDef11{"NOT", "NULL"} //TODO 19 + } +| Expression + { + $$ = $1 //TODO 20 + } + +ColumnDef2: + /* EMPTY */ + { + $$ = nil //TODO 21 + } +| _DEFAULT Expression + { + $$ = []ColumnDef2{"DEFAULT", $2} //TODO 22 + } + +ColumnName: + _IDENTIFIER + { + $$ = $1 //TODO 23 + } + +ColumnNameList: + ColumnName ColumnNameList1 ColumnNameList2 + { + $$ = []ColumnNameList{$1, $2, $3} //TODO 24 + } + +ColumnNameList1: + /* EMPTY */ + { + $$ = []ColumnNameList1(nil) //TODO 25 + } +| ColumnNameList1 ',' ColumnName + { + $$ = append($1.([]ColumnNameList1), ",", $3) //TODO 26 + } + +ColumnNameList2: + /* EMPTY */ + { + $$ = nil //TODO 27 + } +| ',' + { + $$ = "," //TODO 28 + } + +CommitStmt: + _COMMIT + { + $$ = "COMMIT" //TODO 29 + } + +Conversion: + Type '(' Expression ')' + { + $$ = []Conversion{$1, "(", $3, ")"} //TODO 30 + } + +CreateIndexStmt: + _CREATE CreateIndexStmt1 _INDEX CreateIndexStmt2 IndexName _ON TableName '(' ExpressionList ')' + { + $$ = []CreateIndexStmt{"CREATE", $2, "INDEX", $4, $5, "ON", $7, "(", $9, ")"} //TODO 31 + } + +CreateIndexStmt1: + /* EMPTY */ + { + $$ = nil //TODO 32 + } +| _UNIQUE + { + $$ = "UNIQUE" //TODO 33 + } + +CreateIndexStmt2: + /* EMPTY */ + { + $$ = nil //TODO 34 + } +| _IF _NOT _EXISTS + { + $$ = []CreateIndexStmt2{"IF", "NOT", "EXISTS"} //TODO 35 + } + +CreateTableStmt: + _CREATE _TABLE CreateTableStmt1 TableName '(' ColumnDef CreateTableStmt2 CreateTableStmt3 ')' + { + $$ = []CreateTableStmt{"CREATE", "TABLE", $3, $4, "(", $6, $7, $8, ")"} //TODO 36 + } + +CreateTableStmt1: + /* EMPTY */ + { + $$ = nil //TODO 37 + } +| _IF _NOT _EXISTS + { + $$ = []CreateTableStmt1{"IF", "NOT", "EXISTS"} //TODO 38 + } + +CreateTableStmt2: + /* EMPTY */ + { + $$ = []CreateTableStmt2(nil) //TODO 39 + } +| CreateTableStmt2 ',' ColumnDef + { + $$ = append($1.([]CreateTableStmt2), ",", $3) //TODO 40 + } + +CreateTableStmt3: + /* EMPTY */ + { + $$ = nil //TODO 41 + } +| ',' + { + $$ = "," //TODO 42 + } + +DeleteFromStmt: + _DELETE _FROM TableName DeleteFromStmt1 + { + $$ = []DeleteFromStmt{"DELETE", "FROM", $3, $4} //TODO 43 + } + +DeleteFromStmt1: + /* EMPTY */ + { + $$ = nil //TODO 44 + } +| WhereClause + { + $$ = $1 //TODO 45 + } + +DropIndexStmt: + _DROP _INDEX DropIndexStmt1 IndexName + { + $$ = []DropIndexStmt{"DROP", "INDEX", $3, $4} //TODO 46 + } + +DropIndexStmt1: + /* EMPTY */ + { + $$ = nil //TODO 47 + } +| _IF _EXISTS + { + $$ = []DropIndexStmt1{"IF", "EXISTS"} //TODO 48 + } + +DropTableStmt: + _DROP _TABLE DropTableStmt1 TableName + { + $$ = []DropTableStmt{"DROP", "TABLE", $3, $4} //TODO 49 + } + +DropTableStmt1: + /* EMPTY */ + { + $$ = nil //TODO 50 + } +| _IF _EXISTS + { + $$ = []DropTableStmt1{"IF", "EXISTS"} //TODO 51 + } + +EmptyStmt: + /* EMPTY */ + { + $$ = nil //TODO 52 + } + +ExplainStmt: + _EXPLAIN Statement + { + $$ = []ExplainStmt{"EXPLAIN", $2} //TODO 53 + } + +Expression: + Term Expression1 + { + $$ = []Expression{$1, $2} //TODO 54 + } + +Expression1: + /* EMPTY */ + { + $$ = []Expression1(nil) //TODO 55 + } +| Expression1 Expression11 Term + { + $$ = append($1.([]Expression1), $2, $3) //TODO 56 + } + +Expression11: + _OROR + { + $$ = $1 //TODO 57 + } +| _OR + { + $$ = "OR" //TODO 58 + } + +ExpressionList: + Expression ExpressionList1 ExpressionList2 + { + $$ = []ExpressionList{$1, $2, $3} //TODO 59 + } + +ExpressionList1: + /* EMPTY */ + { + $$ = []ExpressionList1(nil) //TODO 60 + } +| ExpressionList1 ',' Expression + { + $$ = append($1.([]ExpressionList1), ",", $3) //TODO 61 + } + +ExpressionList2: + /* EMPTY */ + { + $$ = nil //TODO 62 + } +| ',' + { + $$ = "," //TODO 63 + } + +Factor: + PrimaryFactor Factor1 Factor2 + { + $$ = []Factor{$1, $2, $3} //TODO 64 + } + +Factor1: + /* EMPTY */ + { + $$ = []Factor1(nil) //TODO 65 + } +| Factor1 Factor11 PrimaryFactor + { + $$ = append($1.([]Factor1), $2, $3) //TODO 66 + } + +Factor11: + _GE + { + $$ = $1 //TODO 67 + } +| '>' + { + $$ = ">" //TODO 68 + } +| _LE + { + $$ = $1 //TODO 69 + } +| '<' + { + $$ = "<" //TODO 70 + } +| _NEQ + { + $$ = $1 //TODO 71 + } +| _EQ + { + $$ = $1 //TODO 72 + } +| _LIKE + { + $$ = "LIKE" //TODO 73 + } + +Factor2: + /* EMPTY */ + { + $$ = nil //TODO 74 + } +| Predicate + { + $$ = $1 //TODO 75 + } + +Field: + Expression Field1 + { + $$ = []Field{$1, $2} //TODO 76 + } + +Field1: + /* EMPTY */ + { + $$ = nil //TODO 77 + } +| _AS _IDENTIFIER + { + $$ = []Field1{"AS", $2} //TODO 78 + } + +FieldList: + Field FieldList1 FieldList2 + { + $$ = []FieldList{$1, $2, $3} //TODO 79 + } + +FieldList1: + /* EMPTY */ + { + $$ = []FieldList1(nil) //TODO 80 + } +| FieldList1 ',' Field + { + $$ = append($1.([]FieldList1), ",", $3) //TODO 81 + } + +FieldList2: + /* EMPTY */ + { + $$ = nil //TODO 82 + } +| ',' + { + $$ = "," //TODO 83 + } + +GroupByClause: + _GROUPBY ColumnNameList + { + $$ = []GroupByClause{"GROUP BY", $2} //TODO 84 + } + +Index: + '[' Expression ']' + { + $$ = []Index{"[", $2, "]"} //TODO 85 + } + +IndexName: + _IDENTIFIER + { + $$ = $1 //TODO 86 + } + +InsertIntoStmt: + _INSERT _INTO TableName InsertIntoStmt1 InsertIntoStmt2 + { + $$ = []InsertIntoStmt{"INSERT", "INTO", $3, $4, $5} //TODO 87 + } + +InsertIntoStmt1: + /* EMPTY */ + { + $$ = nil //TODO 88 + } +| '(' ColumnNameList ')' + { + $$ = []InsertIntoStmt1{"(", $2, ")"} //TODO 89 + } + +InsertIntoStmt2: + Values + { + $$ = $1 //TODO 90 + } +| SelectStmt + { + $$ = $1 //TODO 91 + } + +JoinClause: + JoinClause1 JoinClause2 _JOIN RecordSet _ON Expression + { + $$ = []JoinClause{$1, $2, "JOIN", $4, "ON", $6} //TODO 92 + } + +JoinClause1: + _LEFT + { + $$ = "LEFT" //TODO 93 + } +| _RIGHT + { + $$ = "RIGHT" //TODO 94 + } +| _FULL + { + $$ = "FULL" //TODO 95 + } + +JoinClause2: + /* EMPTY */ + { + $$ = nil //TODO 96 + } +| _OUTER + { + $$ = "OUTER" //TODO 97 + } + +Limit: + _LIMIT Expression + { + $$ = []Limit{"Limit", $2} //TODO 98 + } + +Literal: + _FALSE + { + $$ = "FALSE" //TODO 99 + } +| _NULL + { + $$ = "NULL" //TODO 100 + } +| _TRUE + { + $$ = "TRUE" //TODO 101 + } +| _FLOAT_LIT + { + $$ = $1 //TODO 102 + } +| _IMAGINARY_LIT + { + $$ = $1 //TODO 103 + } +| _INT_LIT + { + $$ = $1 //TODO 104 + } +| _RUNE_LIT + { + $$ = $1 //TODO 105 + } +| _STRING_LIT + { + $$ = $1 //TODO 106 + } +| _QL_PARAMETER + { + $$ = $1 //TODO 107 + } + +Offset: + _OFFSET Expression + { + $$ = []Offset{"OFFSET", $2} //TODO 108 + } + +Operand: + Literal + { + $$ = $1 //TODO 109 + } +| QualifiedIdent + { + $$ = $1 //TODO 110 + } +| '(' Expression ')' + { + $$ = []Operand{"(", $2, ")"} //TODO 111 + } + +OrderBy: + _ORDER _BY ExpressionList OrderBy1 + { + $$ = []OrderBy{"ORDER", "BY", $3, $4} //TODO 112 + } + +OrderBy1: + /* EMPTY */ + { + $$ = nil //TODO 113 + } +| OrderBy11 + { + $$ = $1 //TODO 114 + } + +OrderBy11: + _ASC + { + $$ = "ASC" //TODO 115 + } +| _DESC + { + $$ = "DESC" //TODO 116 + } + +Predicate: + Predicate1 + { + $$ = $1 //TODO 117 + } + +Predicate1: + Predicate11 Predicate12 + { + $$ = []Predicate1{$1, $2} //TODO 118 + } +| _IS Predicate13 _NULL + { + $$ = []Predicate1{"IS", $2, "NULL"} //TODO 119 + } + +Predicate11: + /* EMPTY */ + { + $$ = nil //TODO 120 + } +| _NOT + { + $$ = "NOT" //TODO 121 + } + +Predicate12: + _IN '(' ExpressionList ')' + { + $$ = []Predicate12{"IN", "(", $3, ")"} //TODO 122 + } +| _IN '(' SelectStmt Predicate121 ')' + { + $$ = []Predicate12{"IN", "(", $3, $4, ")"} //TODO 123 + } +| _BETWEEN PrimaryFactor _AND PrimaryFactor + { + $$ = []Predicate12{"BETWEEN", $2, "AND", $4} //TODO 124 + } + +Predicate121: + /* EMPTY */ + { + $$ = nil //TODO 125 + } +| ';' + { + $$ = ";" //TODO 126 + } + +Predicate13: + /* EMPTY */ + { + $$ = nil //TODO 127 + } +| _NOT + { + $$ = "NOT" //TODO 128 + } + +PrimaryExpression: + Operand + { + $$ = $1 //TODO 129 + } +| Conversion + { + $$ = $1 //TODO 130 + } +| PrimaryExpression Index + { + $$ = []PrimaryExpression{$1, $2} //TODO 131 + } +| PrimaryExpression Slice + { + $$ = []PrimaryExpression{$1, $2} //TODO 132 + } +| PrimaryExpression Call + { + $$ = []PrimaryExpression{$1, $2} //TODO 133 + } + +PrimaryFactor: + PrimaryTerm PrimaryFactor1 + { + $$ = []PrimaryFactor{$1, $2} //TODO 134 + } + +PrimaryFactor1: + /* EMPTY */ + { + $$ = []PrimaryFactor1(nil) //TODO 135 + } +| PrimaryFactor1 PrimaryFactor11 PrimaryTerm + { + $$ = append($1.([]PrimaryFactor1), $2, $3) //TODO 136 + } + +PrimaryFactor11: + '^' + { + $$ = "^" //TODO 137 + } +| '|' + { + $$ = "|" //TODO 138 + } +| '-' + { + $$ = "-" //TODO 139 + } +| '+' + { + $$ = "+" //TODO 140 + } + +PrimaryTerm: + UnaryExpr PrimaryTerm1 + { + $$ = []PrimaryTerm{$1, $2} //TODO 141 + } + +PrimaryTerm1: + /* EMPTY */ + { + $$ = []PrimaryTerm1(nil) //TODO 142 + } +| PrimaryTerm1 PrimaryTerm11 UnaryExpr + { + $$ = append($1.([]PrimaryTerm1), $2, $3) //TODO 143 + } + +PrimaryTerm11: + _ANDNOT + { + $$ = $1 //TODO 144 + } +| '&' + { + $$ = "&" //TODO 145 + } +| _LSH + { + $$ = $1 //TODO 146 + } +| _RSH + { + $$ = $1 //TODO 147 + } +| '%' + { + $$ = "%" //TODO 148 + } +| '/' + { + $$ = "/" //TODO 149 + } +| '*' + { + $$ = "*" //TODO 150 + } + +QualifiedIdent: + _IDENTIFIER QualifiedIdent1 + { + $$ = []QualifiedIdent{$1, $2} //TODO 151 + } + +QualifiedIdent1: + /* EMPTY */ + { + $$ = nil //TODO 152 + } +| '.' _IDENTIFIER + { + $$ = []QualifiedIdent1{".", $2} //TODO 153 + } + +RecordSet: + RecordSet1 RecordSet2 + { + $$ = []RecordSet{$1, $2} //TODO 154 + } + +RecordSet1: + TableName + { + $$ = $1 //TODO 155 + } +| '(' SelectStmt RecordSet11 ')' + { + $$ = []RecordSet1{"(", $2, $3, ")"} //TODO 156 + } + +RecordSet11: + /* EMPTY */ + { + $$ = nil //TODO 157 + } +| ';' + { + $$ = ";" //TODO 158 + } + +RecordSet2: + /* EMPTY */ + { + $$ = nil //TODO 159 + } +| _AS _IDENTIFIER + { + $$ = []RecordSet2{"AS", $2} //TODO 160 + } + +RecordSetList: + RecordSet RecordSetList1 RecordSetList2 + { + $$ = []RecordSetList{$1, $2, $3} //TODO 161 + } + +RecordSetList1: + /* EMPTY */ + { + $$ = []RecordSetList1(nil) //TODO 162 + } +| RecordSetList1 ',' RecordSet + { + $$ = append($1.([]RecordSetList1), ",", $3) //TODO 163 + } + +RecordSetList2: + /* EMPTY */ + { + $$ = nil //TODO 164 + } +| ',' + { + $$ = "," //TODO 165 + } + +RollbackStmt: + _ROLLBACK + { + $$ = "ROLLBACK" //TODO 166 + } + +SelectStmt: + _SELECT SelectStmt1 SelectStmt2 _FROM RecordSetList SelectStmt3 SelectStmt4 SelectStmt5 SelectStmt6 SelectStmt7 SelectStmt8 + { + $$ = []SelectStmt{"SELECT", $2, $3, "FROM", $5, $6, $7, $8, $9, $10, $11} //TODO 167 + } + +SelectStmt1: + /* EMPTY */ + { + $$ = nil //TODO 168 + } +| _DISTINCT + { + $$ = "DISTINCT" //TODO 169 + } + +SelectStmt2: + '*' + { + $$ = "*" //TODO 170 + } +| FieldList + { + $$ = $1 //TODO 171 + } + +SelectStmt3: + /* EMPTY */ + { + $$ = nil //TODO 172 + } +| JoinClause + { + $$ = $1 //TODO 173 + } + +SelectStmt4: + /* EMPTY */ + { + $$ = nil //TODO 174 + } +| WhereClause + { + $$ = $1 //TODO 175 + } + +SelectStmt5: + /* EMPTY */ + { + $$ = nil //TODO 176 + } +| GroupByClause + { + $$ = $1 //TODO 177 + } + +SelectStmt6: + /* EMPTY */ + { + $$ = nil //TODO 178 + } +| OrderBy + { + $$ = $1 //TODO 179 + } + +SelectStmt7: + /* EMPTY */ + { + $$ = nil //TODO 180 + } +| Limit + { + $$ = $1 //TODO 181 + } + +SelectStmt8: + /* EMPTY */ + { + $$ = nil //TODO 182 + } +| Offset + { + $$ = $1 //TODO 183 + } + +Slice: + '[' Slice1 ':' Slice2 ']' + { + $$ = []Slice{"[", $2, ":", $4, "]"} //TODO 184 + } + +Slice1: + /* EMPTY */ + { + $$ = nil //TODO 185 + } +| Expression + { + $$ = $1 //TODO 186 + } + +Slice2: + /* EMPTY */ + { + $$ = nil //TODO 187 + } +| Expression + { + $$ = $1 //TODO 188 + } + +Start: + StatementList + { + _parserResult = $1 //TODO 189 + } + +Statement: + EmptyStmt + { + $$ = $1 //TODO 190 + } +| AlterTableStmt + { + $$ = $1 //TODO 191 + } +| BeginTransactionStmt + { + $$ = $1 //TODO 192 + } +| CommitStmt + { + $$ = $1 //TODO 193 + } +| CreateIndexStmt + { + $$ = $1 //TODO 194 + } +| CreateTableStmt + { + $$ = $1 //TODO 195 + } +| DeleteFromStmt + { + $$ = $1 //TODO 196 + } +| DropIndexStmt + { + $$ = $1 //TODO 197 + } +| DropTableStmt + { + $$ = $1 //TODO 198 + } +| InsertIntoStmt + { + $$ = $1 //TODO 199 + } +| RollbackStmt + { + $$ = $1 //TODO 200 + } +| SelectStmt + { + $$ = $1 //TODO 201 + } +| TruncateTableStmt + { + $$ = $1 //TODO 202 + } +| UpdateStmt + { + $$ = $1 //TODO 203 + } +| ExplainStmt + { + $$ = $1 //TODO 204 + } + +StatementList: + Statement StatementList1 + { + $$ = []StatementList{$1, $2} //TODO 205 + } + +StatementList1: + /* EMPTY */ + { + $$ = []StatementList1(nil) //TODO 206 + } +| StatementList1 ';' Statement + { + $$ = append($1.([]StatementList1), ";", $3) //TODO 207 + } + +TableName: + _IDENTIFIER + { + $$ = $1 //TODO 208 + } + +Term: + Factor Term1 + { + $$ = []Term{$1, $2} //TODO 209 + } + +Term1: + /* EMPTY */ + { + $$ = []Term1(nil) //TODO 210 + } +| Term1 Term11 Factor + { + $$ = append($1.([]Term1), $2, $3) //TODO 211 + } + +Term11: + _ANDAND + { + $$ = $1 //TODO 212 + } +| _AND + { + $$ = "AND" //TODO 213 + } + +TruncateTableStmt: + _TRUNCATE _TABLE TableName + { + $$ = []TruncateTableStmt{"TRUNCATE", "TABLE", $3} //TODO 214 + } + +Type: + _BIGINT + { + $$ = "bigint" //TODO 215 + } +| _BIGRAT + { + $$ = "bigrat" //TODO 216 + } +| _BLOB + { + $$ = "blob" //TODO 217 + } +| _BOOL + { + $$ = "bool" //TODO 218 + } +| _BYTE + { + $$ = "byte" //TODO 219 + } +| _COMPLEX128 + { + $$ = "complex128" //TODO 220 + } +| _COMPLEX64 + { + $$ = "complex64" //TODO 221 + } +| _DURATION + { + $$ = "duration" //TODO 222 + } +| _FLOAT + { + $$ = "float" //TODO 223 + } +| _FLOAT32 + { + $$ = "float32" //TODO 224 + } +| _FLOAT64 + { + $$ = "float64" //TODO 225 + } +| _INT + { + $$ = "int" //TODO 226 + } +| _INT16 + { + $$ = "int16" //TODO 227 + } +| _INT32 + { + $$ = "int32" //TODO 228 + } +| _INT64 + { + $$ = "int64" //TODO 229 + } +| _INT8 + { + $$ = "int8" //TODO 230 + } +| _RUNE + { + $$ = "rune" //TODO 231 + } +| _STRING + { + $$ = "string" //TODO 232 + } +| _TIME + { + $$ = "time" //TODO 233 + } +| _UINT + { + $$ = "uint" //TODO 234 + } +| _UINT16 + { + $$ = "uint16" //TODO 235 + } +| _UINT32 + { + $$ = "uint32" //TODO 236 + } +| _UINT64 + { + $$ = "uint64" //TODO 237 + } +| _UINT8 + { + $$ = "uint8" //TODO 238 + } + +UnaryExpr: + UnaryExpr1 PrimaryExpression + { + $$ = []UnaryExpr{$1, $2} //TODO 239 + } + +UnaryExpr1: + /* EMPTY */ + { + $$ = nil //TODO 240 + } +| UnaryExpr11 + { + $$ = $1 //TODO 241 + } + +UnaryExpr11: + '^' + { + $$ = "^" //TODO 242 + } +| '!' + { + $$ = "!" //TODO 243 + } +| '-' + { + $$ = "-" //TODO 244 + } +| '+' + { + $$ = "+" //TODO 245 + } + +UpdateStmt: + _UPDATE TableName UpdateStmt1 AssignmentList UpdateStmt2 + { + $$ = []UpdateStmt{"UPDATE", $2, $3, $4, $5} //TODO 246 + } + +UpdateStmt1: + /* EMPTY */ + { + $$ = nil //TODO 247 + } +| _SET + { + $$ = "SET" //TODO 248 + } + +UpdateStmt2: + /* EMPTY */ + { + $$ = nil //TODO 249 + } +| WhereClause + { + $$ = $1 //TODO 250 + } + +Values: + _VALUES '(' ExpressionList ')' Values1 Values2 + { + $$ = []Values{"VALUES", "(", $3, ")", $5, $6} //TODO 251 + } + +Values1: + /* EMPTY */ + { + $$ = []Values1(nil) //TODO 252 + } +| Values1 ',' '(' ExpressionList ')' + { + $$ = append($1.([]Values1), ",", "(", $4, ")") //TODO 253 + } + +Values2: + /* EMPTY */ + { + $$ = nil //TODO 254 + } +| ',' + { + $$ = "," //TODO 255 + } + +WhereClause: + _WHERE Expression + { + $$ = []WhereClause{"WHERE", $2} //TODO 256 + } + +%% + +//TODO remove demo stuff below + +var _parserResult interface{} + +type ( + AlterTableStmt interface{} + AlterTableStmt1 interface{} + Assignment interface{} + AssignmentList interface{} + AssignmentList1 interface{} + AssignmentList2 interface{} + BeginTransactionStmt interface{} + Call interface{} + Call1 interface{} + Call11 interface{} + ColumnDef interface{} + ColumnDef1 interface{} + ColumnDef11 interface{} + ColumnDef2 interface{} + ColumnName interface{} + ColumnNameList interface{} + ColumnNameList1 interface{} + ColumnNameList2 interface{} + CommitStmt interface{} + Conversion interface{} + CreateIndexStmt interface{} + CreateIndexStmt1 interface{} + CreateIndexStmt2 interface{} + CreateTableStmt interface{} + CreateTableStmt1 interface{} + CreateTableStmt2 interface{} + CreateTableStmt3 interface{} + DeleteFromStmt interface{} + DeleteFromStmt1 interface{} + DropIndexStmt interface{} + DropIndexStmt1 interface{} + DropTableStmt interface{} + DropTableStmt1 interface{} + EmptyStmt interface{} + ExplainStmt interface{} + Expression interface{} + Expression1 interface{} + Expression11 interface{} + ExpressionList interface{} + ExpressionList1 interface{} + ExpressionList2 interface{} + Factor interface{} + Factor1 interface{} + Factor11 interface{} + Factor2 interface{} + Field interface{} + Field1 interface{} + FieldList interface{} + FieldList1 interface{} + FieldList2 interface{} + GroupByClause interface{} + Index interface{} + IndexName interface{} + InsertIntoStmt interface{} + InsertIntoStmt1 interface{} + InsertIntoStmt2 interface{} + JoinClause interface{} + JoinClause1 interface{} + JoinClause2 interface{} + Limit interface{} + Literal interface{} + Offset interface{} + Operand interface{} + OrderBy interface{} + OrderBy1 interface{} + OrderBy11 interface{} + Predicate interface{} + Predicate1 interface{} + Predicate11 interface{} + Predicate12 interface{} + Predicate121 interface{} + Predicate13 interface{} + PrimaryExpression interface{} + PrimaryFactor interface{} + PrimaryFactor1 interface{} + PrimaryFactor11 interface{} + PrimaryTerm interface{} + PrimaryTerm1 interface{} + PrimaryTerm11 interface{} + QualifiedIdent interface{} + QualifiedIdent1 interface{} + RecordSet interface{} + RecordSet1 interface{} + RecordSet11 interface{} + RecordSet2 interface{} + RecordSetList interface{} + RecordSetList1 interface{} + RecordSetList2 interface{} + RollbackStmt interface{} + SelectStmt interface{} + SelectStmt1 interface{} + SelectStmt2 interface{} + SelectStmt3 interface{} + SelectStmt4 interface{} + SelectStmt5 interface{} + SelectStmt6 interface{} + SelectStmt7 interface{} + SelectStmt8 interface{} + Slice interface{} + Slice1 interface{} + Slice2 interface{} + Start interface{} + Statement interface{} + StatementList interface{} + StatementList1 interface{} + TableName interface{} + Term interface{} + Term1 interface{} + Term11 interface{} + TruncateTableStmt interface{} + Type interface{} + UnaryExpr interface{} + UnaryExpr1 interface{} + UnaryExpr11 interface{} + UpdateStmt interface{} + UpdateStmt1 interface{} + UpdateStmt2 interface{} + Values interface{} + Values1 interface{} + Values2 interface{} + WhereClause interface{} +) + +func _dump() { + s := fmt.Sprintf("%#v", _parserResult) + s = strings.Replace(s, "%", "%%", -1) + s = strings.Replace(s, "{", "{%i\n", -1) + s = strings.Replace(s, "}", "%u\n}", -1) + s = strings.Replace(s, ", ", ",\n", -1) + var buf bytes.Buffer + strutil.IndentFormatter(&buf, ". ").Format(s) + buf.WriteString("\n") + a := strings.Split(buf.String(), "\n") + for _, v := range a { + if strings.HasSuffix(v, "(nil)") || strings.HasSuffix(v, "(nil),") { + continue + } + + fmt.Println(v) + } +} + +// End of demo stuff diff --git a/Godeps/_workspace/src/github.com/cznic/ql/ql/Makefile b/Godeps/_workspace/src/github.com/cznic/ql/ql/Makefile new file mode 100644 index 000000000..e795538a7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/ql/Makefile @@ -0,0 +1,24 @@ +# Copyright 2014 The ql Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all clean nuke + +all: editor + go vet + go install + make todo + +clean: + go clean + rm -f *~ ql + +editor: + go fmt + go install + +todo: + @grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* *.go || true + @grep -n TODO *.go || true + @grep -n BUG *.go || true + @grep -n println *.go || true diff --git a/Godeps/_workspace/src/github.com/cznic/ql/ql/README.md b/Godeps/_workspace/src/github.com/cznic/ql/ql/README.md new file mode 100644 index 000000000..07139fd61 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/ql/README.md @@ -0,0 +1,10 @@ +ql +== + +Command ql is a utility to explore a database, prototype a schema or test drive a query, etc. + +Installation + + $ go get github.com/cznic/ql/ql + +Documentation: [godoc.org/github.com/cznic/ql/ql](http://godoc.org/github.com/cznic/ql/ql) diff --git a/Godeps/_workspace/src/github.com/cznic/ql/ql/main.go b/Godeps/_workspace/src/github.com/cznic/ql/ql/main.go new file mode 100644 index 000000000..568fd706e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/ql/main.go @@ -0,0 +1,219 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Command ql is a utility to explore a database, prototype a schema or test +// drive a query, etc. +// +// Installation: +// +// $ go get github.com/cznic/ql/ql +// +// Usage: +// +// ql [-db name] [-schema regexp] [-tables regexp] [-fld] statement_list +// +// Options: +// +// -db name Name of the database to use. Defaults to "ql.db". +// If the DB file does not exists it is created automatically. +// +// -schema re If re != "" show the CREATE statements of matching tables and exit. +// +// -tables re If re != "" show the matching table names and exit. +// +// -fld First row of a query result set will show field names. +// +// statement_list QL statements to execute. +// If no non flag arguments are present, ql reads from stdin. +// The list is wrapped into an automatic transaction. +// +// -t Report and measure time to execute, including creating/opening and closing the DB. +// +// Example: +// +// $ ql 'create table t (i int, s string)' +// $ ql << EOF +// > insert into t values +// > (1, "a"), +// > (2, "b"), +// > (3, "c"), +// > EOF +// $ ql 'select * from t' +// 3, "c" +// 2, "b" +// 1, "a" +// $ ql -fld 'select * from t where i != 2 order by s' +// "i", "s" +// 1, "a" +// 3, "c" +// $ +package main + +import ( + "bufio" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "regexp" + "sort" + "strings" + "time" + + "github.com/cznic/ql" +) + +func str(data []interface{}) string { + a := make([]string, len(data)) + for i, v := range data { + switch x := v.(type) { + case string: + a[i] = fmt.Sprintf("%q", x) + default: + a[i] = fmt.Sprint(x) + } + } + return strings.Join(a, ", ") +} + +func main() { + if err := do(); err != nil { + log.Fatal(err) + } +} + +func do() (err error) { + oDB := flag.String("db", "ql.db", "The DB file to open. It'll be created if missing.") + oFlds := flag.Bool("fld", false, "Show recordset's field names.") + oSchema := flag.String("schema", "", "If non empty, show the CREATE statements of matching tables and exit.") + oTables := flag.String("tables", "", "If non empty, list matching table names and exit.") + oTime := flag.Bool("t", false, "Measure and report time to execute the statement(s) including DB create/open/close.") + flag.Parse() + + t0 := time.Now() + if *oTime { + defer func() { + fmt.Fprintf(os.Stderr, "%s\n", time.Since(t0)) + }() + } + + db, err := ql.OpenFile(*oDB, &ql.Options{CanCreate: true}) + if err != nil { + return err + } + + defer func() { + ec := db.Close() + switch { + case ec != nil && err != nil: + log.Println(ec) + case ec != nil: + err = ec + } + }() + + if pat := *oSchema; pat != "" { + re, err := regexp.Compile(pat) + if err != nil { + return err + } + + nfo, err := db.Info() + if err != nil { + return err + } + + r := []string{} + for _, ti := range nfo.Tables { + if !re.MatchString(ti.Name) { + continue + } + + a := []string{} + for _, ci := range ti.Columns { + a = append(a, fmt.Sprintf("%s %s", ci.Name, ci.Type)) + } + r = append(r, fmt.Sprintf("CREATE TABLE %s (%s);", ti.Name, strings.Join(a, ", "))) + } + sort.Strings(r) + if len(r) != 0 { + fmt.Println(strings.Join(r, "\n")) + } + return nil + } + + if pat := *oTables; pat != "" { + re, err := regexp.Compile(pat) + if err != nil { + return err + } + + nfo, err := db.Info() + if err != nil { + return err + } + + r := []string{} + for _, ti := range nfo.Tables { + if !re.MatchString(ti.Name) { + continue + } + + r = append(r, ti.Name) + } + sort.Strings(r) + if len(r) != 0 { + fmt.Println(strings.Join(r, "\n")) + } + return nil + } + + var src string + switch n := flag.NArg(); n { + case 0: + b, err := ioutil.ReadAll(bufio.NewReader(os.Stdin)) + if err != nil { + return err + } + + src = string(b) + default: + a := make([]string, n) + for i := range a { + a[i] = flag.Arg(i) + } + src = strings.Join(a, " ") + } + + src = "BEGIN TRANSACTION; " + src + "; COMMIT;" + l, err := ql.Compile(src) + if err != nil { + log.Println(src) + return err + } + + rs, i, err := db.Execute(ql.NewRWCtx(), l) + if err != nil { + a := strings.Split(strings.TrimSpace(fmt.Sprint(l)), "\n") + return fmt.Errorf("%v: %s", err, a[i]) + } + + if len(rs) == 0 { + return + } + + switch { + case l.IsExplainStmt(): + return rs[len(rs)-1].Do(*oFlds, func(data []interface{}) (bool, error) { + fmt.Println(data[0]) + return true, nil + }) + default: + return rs[len(rs)-1].Do(*oFlds, func(data []interface{}) (bool, error) { + fmt.Println(str(data)) + return true, nil + }) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/scanner.go b/Godeps/_workspace/src/github.com/cznic/ql/scanner.go new file mode 100644 index 000000000..aafa3cf8f --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/scanner.go @@ -0,0 +1,4129 @@ +// CAUTION: Generated file - DO NOT EDIT. + +/* +Copyright (c) 2014 The ql Authors. All rights reserved. +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file. +*/ + +package ql + +import ( + "fmt" + "math" + "strconv" + "unicode" +) + +type lexer struct { + agg []bool + c int + col int + errs []error + expr expression + i int + inj int + lcol int + line int + list []stmt + ncol int + nline int + params int + sc int + src string + val []byte + root bool +} + +func newLexer(src string) (l *lexer) { + l = &lexer{ + src: src, + nline: 1, + ncol: 0, + } + l.next() + return +} + +func (l *lexer) next() int { + if l.c != 0 { + l.val = append(l.val, byte(l.c)) + } + l.c = 0 + if l.i < len(l.src) { + l.c = int(l.src[l.i]) + l.i++ + } + switch l.c { + case '\n': + l.lcol = l.ncol + l.nline++ + l.ncol = 0 + default: + l.ncol++ + } + return l.c +} + +func (l *lexer) err0(ln, c int, s string, arg ...interface{}) { + err := fmt.Errorf(fmt.Sprintf("%d:%d ", ln, c)+s, arg...) + l.errs = append(l.errs, err) +} + +func (l *lexer) err(s string, arg ...interface{}) { + l.err0(l.line, l.col, s, arg...) +} + +func (l *lexer) Error(s string) { + l.err(s) +} + +func (l *lexer) Lex(lval *yySymType) (r int) { + //defer func() { dbg("Lex -> %d(%#x)", r, r) }() + defer func() { + lval.line, lval.col = l.line, l.col + }() + const ( + INITIAL = iota + S1 + S2 + ) + + if n := l.inj; n != 0 { + l.inj = 0 + return n + } + + c0, c := 0, l.c + +yystate0: + + l.val = l.val[:0] + c0, l.line, l.col = l.c, l.nline, l.ncol + + switch yyt := l.sc; yyt { + default: + panic(fmt.Errorf(`invalid start condition %d`, yyt)) + case 0: // start condition: INITIAL + goto yystart1 + case 1: // start condition: S1 + goto yystart319 + case 2: // start condition: S2 + goto yystart324 + } + + goto yystate0 // silence unused label error + goto yystate1 // silence unused label error +yystate1: + c = l.next() +yystart1: + switch { + default: + goto yystate3 // c >= '\x01' && c <= '\b' || c == '\v' || c == '\f' || c >= '\x0e' && c <= '\x1f' || c == '#' || c == '%%' || c >= '(' && c <= ',' || c == ':' || c == ';' || c == '@' || c >= '[' && c <= '^' || c == '{' || c >= '}' && c <= 'ÿ' + case c == '!': + goto yystate6 + case c == '"': + goto yystate8 + case c == '$' || c == '?': + goto yystate9 + case c == '&': + goto yystate11 + case c == '-': + goto yystate19 + case c == '.': + goto yystate21 + case c == '/': + goto yystate27 + case c == '0': + goto yystate32 + case c == '<': + goto yystate40 + case c == '=': + goto yystate43 + case c == '>': + goto yystate45 + case c == 'A' || c == 'a': + goto yystate48 + case c == 'B' || c == 'b': + goto yystate60 + case c == 'C' || c == 'c': + goto yystate87 + case c == 'D' || c == 'd': + goto yystate111 + case c == 'E' || c == 'e': + goto yystate141 + case c == 'F' || c == 'f': + goto yystate152 + case c == 'G' || c == 'g': + goto yystate171 + case c == 'H' || c == 'K' || c == 'M' || c == 'P' || c == 'Q' || c >= 'X' && c <= 'Z' || c == '_' || c == 'h' || c == 'k' || c == 'm' || c == 'p' || c == 'q' || c >= 'x' && c <= 'z': + goto yystate176 + case c == 'I' || c == 'i': + goto yystate177 + case c == 'J' || c == 'j': + goto yystate197 + case c == 'L' || c == 'l': + goto yystate201 + case c == 'N' || c == 'n': + goto yystate211 + case c == 'O' || c == 'o': + goto yystate217 + case c == 'R' || c == 'r': + goto yystate232 + case c == 'S' || c == 's': + goto yystate247 + case c == 'T' || c == 't': + goto yystate259 + case c == 'U' || c == 'u': + goto yystate284 + case c == 'V' || c == 'v': + goto yystate305 + case c == 'W' || c == 'w': + goto yystate311 + case c == '\'': + goto yystate14 + case c == '\n': + goto yystate5 + case c == '\t' || c == '\r' || c == ' ': + goto yystate4 + case c == '\x00': + goto yystate2 + case c == '`': + goto yystate316 + case c == '|': + goto yystate317 + case c >= '1' && c <= '9': + goto yystate38 + } + +yystate2: + c = l.next() + goto yyrule1 + +yystate3: + c = l.next() + goto yyrule101 + +yystate4: + c = l.next() + switch { + default: + goto yyrule2 + case c == '\t' || c == '\n' || c == '\r' || c == ' ': + goto yystate5 + } + +yystate5: + c = l.next() + switch { + default: + goto yyrule2 + case c == '\t' || c == '\n' || c == '\r' || c == ' ': + goto yystate5 + } + +yystate6: + c = l.next() + switch { + default: + goto yyrule101 + case c == '=': + goto yystate7 + } + +yystate7: + c = l.next() + goto yyrule21 + +yystate8: + c = l.next() + goto yyrule10 + +yystate9: + c = l.next() + switch { + default: + goto yyrule101 + case c >= '0' && c <= '9': + goto yystate10 + } + +yystate10: + c = l.next() + switch { + default: + goto yyrule100 + case c >= '0' && c <= '9': + goto yystate10 + } + +yystate11: + c = l.next() + switch { + default: + goto yyrule101 + case c == '&': + goto yystate12 + case c == '^': + goto yystate13 + } + +yystate12: + c = l.next() + goto yyrule15 + +yystate13: + c = l.next() + goto yyrule16 + +yystate14: + c = l.next() + switch { + default: + goto yyrule101 + case c == '\'': + goto yystate16 + case c == '\\': + goto yystate17 + case c >= '\x01' && c <= '&' || c >= '(' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate15 + } + +yystate15: + c = l.next() + switch { + default: + goto yyabort + case c == '\'': + goto yystate16 + case c == '\\': + goto yystate17 + case c >= '\x01' && c <= '&' || c >= '(' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate15 + } + +yystate16: + c = l.next() + goto yyrule12 + +yystate17: + c = l.next() + switch { + default: + goto yyabort + case c == '\'': + goto yystate18 + case c == '\\': + goto yystate17 + case c >= '\x01' && c <= '&' || c >= '(' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate15 + } + +yystate18: + c = l.next() + switch { + default: + goto yyrule12 + case c == '\'': + goto yystate16 + case c == '\\': + goto yystate17 + case c >= '\x01' && c <= '&' || c >= '(' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate15 + } + +yystate19: + c = l.next() + switch { + default: + goto yyrule101 + case c == '-': + goto yystate20 + } + +yystate20: + c = l.next() + switch { + default: + goto yyrule3 + case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': + goto yystate20 + } + +yystate21: + c = l.next() + switch { + default: + goto yyrule101 + case c >= '0' && c <= '9': + goto yystate22 + } + +yystate22: + c = l.next() + switch { + default: + goto yyrule9 + case c == 'E' || c == 'e': + goto yystate23 + case c == 'i': + goto yystate26 + case c >= '0' && c <= '9': + goto yystate22 + } + +yystate23: + c = l.next() + switch { + default: + goto yyabort + case c == '+' || c == '-': + goto yystate24 + case c >= '0' && c <= '9': + goto yystate25 + } + +yystate24: + c = l.next() + switch { + default: + goto yyabort + case c >= '0' && c <= '9': + goto yystate25 + } + +yystate25: + c = l.next() + switch { + default: + goto yyrule9 + case c == 'i': + goto yystate26 + case c >= '0' && c <= '9': + goto yystate25 + } + +yystate26: + c = l.next() + goto yyrule7 + +yystate27: + c = l.next() + switch { + default: + goto yyrule101 + case c == '*': + goto yystate28 + case c == '/': + goto yystate31 + } + +yystate28: + c = l.next() + switch { + default: + goto yyabort + case c == '*': + goto yystate29 + case c >= '\x01' && c <= ')' || c >= '+' && c <= 'ÿ': + goto yystate28 + } + +yystate29: + c = l.next() + switch { + default: + goto yyabort + case c == '*': + goto yystate29 + case c == '/': + goto yystate30 + case c >= '\x01' && c <= ')' || c >= '+' && c <= '.' || c >= '0' && c <= 'ÿ': + goto yystate28 + } + +yystate30: + c = l.next() + goto yyrule5 + +yystate31: + c = l.next() + switch { + default: + goto yyrule4 + case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': + goto yystate31 + } + +yystate32: + c = l.next() + switch { + default: + goto yyrule8 + case c == '.': + goto yystate22 + case c == '8' || c == '9': + goto yystate34 + case c == 'E' || c == 'e': + goto yystate23 + case c == 'X' || c == 'x': + goto yystate36 + case c == 'i': + goto yystate35 + case c >= '0' && c <= '7': + goto yystate33 + } + +yystate33: + c = l.next() + switch { + default: + goto yyrule8 + case c == '.': + goto yystate22 + case c == '8' || c == '9': + goto yystate34 + case c == 'E' || c == 'e': + goto yystate23 + case c == 'i': + goto yystate35 + case c >= '0' && c <= '7': + goto yystate33 + } + +yystate34: + c = l.next() + switch { + default: + goto yyabort + case c == '.': + goto yystate22 + case c == 'E' || c == 'e': + goto yystate23 + case c == 'i': + goto yystate35 + case c >= '0' && c <= '9': + goto yystate34 + } + +yystate35: + c = l.next() + goto yyrule6 + +yystate36: + c = l.next() + switch { + default: + goto yyabort + case c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f': + goto yystate37 + } + +yystate37: + c = l.next() + switch { + default: + goto yyrule8 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f': + goto yystate37 + } + +yystate38: + c = l.next() + switch { + default: + goto yyrule8 + case c == '.': + goto yystate22 + case c == 'E' || c == 'e': + goto yystate23 + case c == 'i': + goto yystate35 + case c >= '0' && c <= '9': + goto yystate39 + } + +yystate39: + c = l.next() + switch { + default: + goto yyrule8 + case c == '.': + goto yystate22 + case c == 'E' || c == 'e': + goto yystate23 + case c == 'i': + goto yystate35 + case c >= '0' && c <= '9': + goto yystate39 + } + +yystate40: + c = l.next() + switch { + default: + goto yyrule101 + case c == '<': + goto yystate41 + case c == '=': + goto yystate42 + } + +yystate41: + c = l.next() + goto yyrule17 + +yystate42: + c = l.next() + goto yyrule18 + +yystate43: + c = l.next() + switch { + default: + goto yyrule101 + case c == '=': + goto yystate44 + } + +yystate44: + c = l.next() + goto yyrule19 + +yystate45: + c = l.next() + switch { + default: + goto yyrule101 + case c == '=': + goto yystate46 + case c == '>': + goto yystate47 + } + +yystate46: + c = l.next() + goto yyrule20 + +yystate47: + c = l.next() + goto yyrule23 + +yystate48: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'D' || c == 'd': + goto yystate50 + case c == 'L' || c == 'l': + goto yystate52 + case c == 'N' || c == 'n': + goto yystate56 + case c == 'S' || c == 's': + goto yystate58 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'K' || c == 'M' || c >= 'O' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'k' || c == 'm' || c >= 'o' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate49: + c = l.next() + switch { + default: + goto yyrule99 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate50: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'D' || c == 'd': + goto yystate51 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'z': + goto yystate49 + } + +yystate51: + c = l.next() + switch { + default: + goto yyrule24 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate52: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate53 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate53: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate54 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate54: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'R' || c == 'r': + goto yystate55 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate55: + c = l.next() + switch { + default: + goto yyrule25 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate56: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'D' || c == 'd': + goto yystate57 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'z': + goto yystate49 + } + +yystate57: + c = l.next() + switch { + default: + goto yyrule26 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate58: + c = l.next() + switch { + default: + goto yyrule28 + case c == 'C' || c == 'c': + goto yystate59 + case c >= '0' && c <= '9' || c == 'A' || c == 'B' || c >= 'D' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': + goto yystate49 + } + +yystate59: + c = l.next() + switch { + default: + goto yyrule27 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate60: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate61 + case c == 'I' || c == 'i': + goto yystate70 + case c == 'L' || c == 'l': + goto yystate78 + case c == 'O' || c == 'o': + goto yystate81 + case c == 'Y' || c == 'y': + goto yystate84 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'H' || c == 'J' || c == 'K' || c == 'M' || c == 'N' || c >= 'P' && c <= 'X' || c == 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'h' || c == 'j' || c == 'k' || c == 'm' || c == 'n' || c >= 'p' && c <= 'x' || c == 'z': + goto yystate49 + } + +yystate61: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'G' || c == 'g': + goto yystate62 + case c == 'T' || c == 't': + goto yystate65 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'H' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate62: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate63 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate63: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate64 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate64: + c = l.next() + switch { + default: + goto yyrule29 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate65: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'W' || c == 'w': + goto yystate66 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'V' || c >= 'X' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'v' || c >= 'x' && c <= 'z': + goto yystate49 + } + +yystate66: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate67 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate67: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate68 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate68: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate69 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate69: + c = l.next() + switch { + default: + goto yyrule30 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate70: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'G' || c == 'g': + goto yystate71 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'H' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': + goto yystate49 + } + +yystate71: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate72 + case c == 'R' || c == 'r': + goto yystate75 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate72: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate73 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate73: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate74 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate74: + c = l.next() + switch { + default: + goto yyrule75 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate75: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate76 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate76: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate77 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate77: + c = l.next() + switch { + default: + goto yyrule76 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate78: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate79 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + } + +yystate79: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'B' || c == 'b': + goto yystate80 + case c >= '0' && c <= '9' || c == 'A' || c >= 'C' && c <= 'Z' || c == '_' || c == 'a' || c >= 'c' && c <= 'z': + goto yystate49 + } + +yystate80: + c = l.next() + switch { + default: + goto yyrule77 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate81: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate82 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + } + +yystate82: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate83 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate83: + c = l.next() + switch { + default: + goto yyrule78 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate84: + c = l.next() + switch { + default: + goto yyrule31 + case c == 'T' || c == 't': + goto yystate85 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate85: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate86 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate86: + c = l.next() + switch { + default: + goto yyrule79 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate87: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate88 + case c == 'R' || c == 'r': + goto yystate106 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c == 'P' || c == 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c == 'p' || c == 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate88: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate89 + case c == 'M' || c == 'm': + goto yystate93 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'n' && c <= 'z': + goto yystate49 + } + +yystate89: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'U' || c == 'u': + goto yystate90 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate90: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'M' || c == 'm': + goto yystate91 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': + goto yystate49 + } + +yystate91: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate92 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate92: + c = l.next() + switch { + default: + goto yyrule32 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate93: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'M' || c == 'm': + goto yystate94 + case c == 'P' || c == 'p': + goto yystate97 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'L' || c == 'N' || c == 'O' || c >= 'Q' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c == 'n' || c == 'o' || c >= 'q' && c <= 'z': + goto yystate49 + } + +yystate94: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate95 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate95: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate96 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate96: + c = l.next() + switch { + default: + goto yyrule33 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate97: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate98 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate98: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate99 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate99: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'X' || c == 'x': + goto yystate100 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'W' || c == 'Y' || c == 'Z' || c == '_' || c >= 'a' && c <= 'w' || c == 'y' || c == 'z': + goto yystate49 + } + +yystate100: + c = l.next() + switch { + default: + goto yyrule99 + case c == '0' || c >= '2' && c <= '5' || c >= '7' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + case c == '1': + goto yystate101 + case c == '6': + goto yystate104 + } + +yystate101: + c = l.next() + switch { + default: + goto yyrule99 + case c == '0' || c == '1' || c >= '3' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + case c == '2': + goto yystate102 + } + +yystate102: + c = l.next() + switch { + default: + goto yyrule99 + case c == '8': + goto yystate103 + case c >= '0' && c <= '7' || c == '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate103: + c = l.next() + switch { + default: + goto yyrule80 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate104: + c = l.next() + switch { + default: + goto yyrule99 + case c == '4': + goto yystate105 + case c >= '0' && c <= '3' || c >= '5' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate105: + c = l.next() + switch { + default: + goto yyrule81 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate106: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate107 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate107: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate108 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate108: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate109 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate109: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate110 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate110: + c = l.next() + switch { + default: + goto yyrule34 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate111: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate112 + case c == 'I' || c == 'i': + goto yystate124 + case c == 'R' || c == 'r': + goto yystate131 + case c == 'U' || c == 'u': + goto yystate134 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'H' || c >= 'J' && c <= 'Q' || c == 'S' || c == 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'h' || c >= 'j' && c <= 'q' || c == 's' || c == 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate112: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'F' || c == 'f': + goto yystate113 + case c == 'L' || c == 'l': + goto yystate118 + case c == 'S' || c == 's': + goto yystate122 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'E' || c >= 'G' && c <= 'K' || c >= 'M' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'k' || c >= 'm' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate113: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate114 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate114: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'U' || c == 'u': + goto yystate115 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate115: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate116 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate116: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate117 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate117: + c = l.next() + switch { + default: + goto yyrule35 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate118: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate119 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate119: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate120 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate120: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate121 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate121: + c = l.next() + switch { + default: + goto yyrule36 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate122: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'C' || c == 'c': + goto yystate123 + case c >= '0' && c <= '9' || c == 'A' || c == 'B' || c >= 'D' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': + goto yystate49 + } + +yystate123: + c = l.next() + switch { + default: + goto yyrule37 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate124: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'S' || c == 's': + goto yystate125 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate125: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate126 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate126: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate127 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate127: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate128 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate128: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'C' || c == 'c': + goto yystate129 + case c >= '0' && c <= '9' || c == 'A' || c == 'B' || c >= 'D' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': + goto yystate49 + } + +yystate129: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate130 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate130: + c = l.next() + switch { + default: + goto yyrule38 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate131: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate132 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + } + +yystate132: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'P' || c == 'p': + goto yystate133 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'O' || c >= 'Q' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'o' || c >= 'q' && c <= 'z': + goto yystate49 + } + +yystate133: + c = l.next() + switch { + default: + goto yyrule39 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate134: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'R' || c == 'r': + goto yystate135 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate135: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate136 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate136: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate137 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate137: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate138 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate138: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate139 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + } + +yystate139: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate140 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate140: + c = l.next() + switch { + default: + goto yyrule82 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate141: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'X' || c == 'x': + goto yystate142 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'W' || c == 'Y' || c == 'Z' || c == '_' || c >= 'a' && c <= 'w' || c == 'y' || c == 'z': + goto yystate49 + } + +yystate142: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate143 + case c == 'P' || c == 'p': + goto yystate147 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'O' || c >= 'Q' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'o' || c >= 'q' && c <= 'z': + goto yystate49 + } + +yystate143: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'S' || c == 's': + goto yystate144 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate144: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate145 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate145: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'S' || c == 's': + goto yystate146 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate146: + c = l.next() + switch { + default: + goto yyrule40 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate147: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate148 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate148: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate149 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate149: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate150 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate150: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate151 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate151: + c = l.next() + switch { + default: + goto yyrule41 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate152: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate153 + case c == 'L' || c == 'l': + goto yystate157 + case c == 'R' || c == 'r': + goto yystate165 + case c == 'U' || c == 'u': + goto yystate168 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'K' || c >= 'M' && c <= 'Q' || c == 'S' || c == 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'k' || c >= 'm' && c <= 'q' || c == 's' || c == 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate153: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate154 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate154: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'S' || c == 's': + goto yystate155 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate155: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate156 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate156: + c = l.next() + switch { + default: + goto yyrule73 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate157: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate158 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + } + +yystate158: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate159 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate159: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate160 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate160: + c = l.next() + switch { + default: + goto yyrule83 + case c == '3': + goto yystate161 + case c == '6': + goto yystate163 + case c >= '0' && c <= '2' || c == '4' || c == '5' || c >= '7' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate161: + c = l.next() + switch { + default: + goto yyrule99 + case c == '0' || c == '1' || c >= '3' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + case c == '2': + goto yystate162 + } + +yystate162: + c = l.next() + switch { + default: + goto yyrule84 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate163: + c = l.next() + switch { + default: + goto yyrule99 + case c == '4': + goto yystate164 + case c >= '0' && c <= '3' || c >= '5' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate164: + c = l.next() + switch { + default: + goto yyrule85 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate165: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate166 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + } + +yystate166: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'M' || c == 'm': + goto yystate167 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': + goto yystate49 + } + +yystate167: + c = l.next() + switch { + default: + goto yyrule42 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate168: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate169 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate169: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate170 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate170: + c = l.next() + switch { + default: + goto yyrule43 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate171: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'R' || c == 'r': + goto yystate172 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate172: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate173 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + } + +yystate173: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'U' || c == 'u': + goto yystate174 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate174: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'P' || c == 'p': + goto yystate175 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'O' || c >= 'Q' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'o' || c >= 'q' && c <= 'z': + goto yystate49 + } + +yystate175: + c = l.next() + switch { + default: + goto yyrule44 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate176: + c = l.next() + switch { + default: + goto yyrule99 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate177: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'F' || c == 'f': + goto yystate178 + case c == 'N' || c == 'n': + goto yystate179 + case c == 'S' || c == 's': + goto yystate196 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'E' || c >= 'G' && c <= 'M' || c >= 'O' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'm' || c >= 'o' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate178: + c = l.next() + switch { + default: + goto yyrule45 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate179: + c = l.next() + switch { + default: + goto yyrule49 + case c == 'D' || c == 'd': + goto yystate180 + case c == 'S' || c == 's': + goto yystate183 + case c == 'T' || c == 't': + goto yystate187 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'R' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'r' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate180: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate181 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate181: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'X' || c == 'x': + goto yystate182 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'W' || c == 'Y' || c == 'Z' || c == '_' || c >= 'a' && c <= 'w' || c == 'y' || c == 'z': + goto yystate49 + } + +yystate182: + c = l.next() + switch { + default: + goto yyrule46 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate183: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate184 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate184: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'R' || c == 'r': + goto yystate185 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate185: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate186 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate186: + c = l.next() + switch { + default: + goto yyrule47 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate187: + c = l.next() + switch { + default: + goto yyrule86 + case c == '0' || c == '2' || c == '4' || c == '5' || c == '7' || c == '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + case c == '1': + goto yystate188 + case c == '3': + goto yystate190 + case c == '6': + goto yystate192 + case c == '8': + goto yystate194 + case c == 'O' || c == 'o': + goto yystate195 + } + +yystate188: + c = l.next() + switch { + default: + goto yyrule99 + case c == '6': + goto yystate189 + case c >= '0' && c <= '5' || c >= '7' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate189: + c = l.next() + switch { + default: + goto yyrule87 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate190: + c = l.next() + switch { + default: + goto yyrule99 + case c == '0' || c == '1' || c >= '3' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + case c == '2': + goto yystate191 + } + +yystate191: + c = l.next() + switch { + default: + goto yyrule88 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate192: + c = l.next() + switch { + default: + goto yyrule99 + case c == '4': + goto yystate193 + case c >= '0' && c <= '3' || c >= '5' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate193: + c = l.next() + switch { + default: + goto yyrule89 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate194: + c = l.next() + switch { + default: + goto yyrule90 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate195: + c = l.next() + switch { + default: + goto yyrule48 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate196: + c = l.next() + switch { + default: + goto yyrule50 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate197: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate198 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + } + +yystate198: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate199 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate199: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate200 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate200: + c = l.next() + switch { + default: + goto yyrule51 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate201: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate202 + case c == 'I' || c == 'i': + goto yystate205 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate202: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'F' || c == 'f': + goto yystate203 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'E' || c >= 'G' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'z': + goto yystate49 + } + +yystate203: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate204 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate204: + c = l.next() + switch { + default: + goto yyrule52 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate205: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'K' || c == 'k': + goto yystate206 + case c == 'M' || c == 'm': + goto yystate208 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'J' || c == 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'j' || c == 'l' || c >= 'n' && c <= 'z': + goto yystate49 + } + +yystate206: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate207 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate207: + c = l.next() + switch { + default: + goto yyrule53 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate208: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate209 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate209: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate210 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate210: + c = l.next() + switch { + default: + goto yyrule54 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate211: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate212 + case c == 'U' || c == 'u': + goto yystate214 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate212: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate213 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate213: + c = l.next() + switch { + default: + goto yyrule55 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate214: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate215 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate215: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate216 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate216: + c = l.next() + switch { + default: + goto yyrule72 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate217: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'F' || c == 'f': + goto yystate218 + case c == 'N' || c == 'n': + goto yystate223 + case c == 'R' || c == 'r': + goto yystate224 + case c == 'U' || c == 'u': + goto yystate228 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'E' || c >= 'G' && c <= 'M' || c >= 'O' && c <= 'Q' || c == 'S' || c == 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'm' || c >= 'o' && c <= 'q' || c == 's' || c == 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate218: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'F' || c == 'f': + goto yystate219 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'E' || c >= 'G' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'z': + goto yystate49 + } + +yystate219: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'S' || c == 's': + goto yystate220 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate220: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate221 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate221: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate222 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate222: + c = l.next() + switch { + default: + goto yyrule56 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate223: + c = l.next() + switch { + default: + goto yyrule57 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate224: + c = l.next() + switch { + default: + goto yyrule59 + case c == 'D' || c == 'd': + goto yystate225 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'z': + goto yystate49 + } + +yystate225: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate226 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate226: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'R' || c == 'r': + goto yystate227 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate227: + c = l.next() + switch { + default: + goto yyrule58 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate228: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate229 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate229: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate230 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate230: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'R' || c == 'r': + goto yystate231 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate231: + c = l.next() + switch { + default: + goto yyrule60 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate232: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate233 + case c == 'O' || c == 'o': + goto yystate237 + case c == 'U' || c == 'u': + goto yystate244 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'N' || c >= 'P' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'n' || c >= 'p' && c <= 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate233: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'G' || c == 'g': + goto yystate234 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'H' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': + goto yystate49 + } + +yystate234: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'H' || c == 'h': + goto yystate235 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'G' || c >= 'I' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'g' || c >= 'i' && c <= 'z': + goto yystate49 + } + +yystate235: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate236 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate236: + c = l.next() + switch { + default: + goto yyrule61 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate237: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate238 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate238: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate239 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate239: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'B' || c == 'b': + goto yystate240 + case c >= '0' && c <= '9' || c == 'A' || c >= 'C' && c <= 'Z' || c == '_' || c == 'a' || c >= 'c' && c <= 'z': + goto yystate49 + } + +yystate240: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate241 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate241: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'C' || c == 'c': + goto yystate242 + case c >= '0' && c <= '9' || c == 'A' || c == 'B' || c >= 'D' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': + goto yystate49 + } + +yystate242: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'K' || c == 'k': + goto yystate243 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'J' || c >= 'L' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'j' || c >= 'l' && c <= 'z': + goto yystate49 + } + +yystate243: + c = l.next() + switch { + default: + goto yyrule62 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate244: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate245 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate245: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate246 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate246: + c = l.next() + switch { + default: + goto yyrule91 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate247: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate248 + case c == 'T' || c == 't': + goto yystate254 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate248: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate249 + case c == 'T' || c == 't': + goto yystate253 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate249: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate250 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate250: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'C' || c == 'c': + goto yystate251 + case c >= '0' && c <= '9' || c == 'A' || c == 'B' || c >= 'D' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': + goto yystate49 + } + +yystate251: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate252 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate252: + c = l.next() + switch { + default: + goto yyrule63 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate253: + c = l.next() + switch { + default: + goto yyrule64 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate254: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'R' || c == 'r': + goto yystate255 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate255: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate256 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate256: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate257 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate257: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'G' || c == 'g': + goto yystate258 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'H' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': + goto yystate49 + } + +yystate258: + c = l.next() + switch { + default: + goto yyrule92 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate259: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate260 + case c == 'I' || c == 'i': + goto yystate264 + case c == 'R' || c == 'r': + goto yystate267 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'H' || c >= 'J' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'h' || c >= 'j' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate260: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'B' || c == 'b': + goto yystate261 + case c >= '0' && c <= '9' || c == 'A' || c >= 'C' && c <= 'Z' || c == '_' || c == 'a' || c >= 'c' && c <= 'z': + goto yystate49 + } + +yystate261: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate262 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate262: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate263 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate263: + c = l.next() + switch { + default: + goto yyrule65 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate264: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'M' || c == 'm': + goto yystate265 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': + goto yystate49 + } + +yystate265: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate266 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate266: + c = l.next() + switch { + default: + goto yyrule93 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate267: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate268 + case c == 'U' || c == 'u': + goto yystate277 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'b' && c <= 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate268: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate269 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate269: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'S' || c == 's': + goto yystate270 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate270: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate271 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate271: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'C' || c == 'c': + goto yystate272 + case c >= '0' && c <= '9' || c == 'A' || c == 'B' || c >= 'D' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': + goto yystate49 + } + +yystate272: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate273 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate273: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate274 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate274: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'O' || c == 'o': + goto yystate275 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate49 + } + +yystate275: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate276 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate276: + c = l.next() + switch { + default: + goto yyrule66 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate277: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate278 + case c == 'N' || c == 'n': + goto yystate279 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate278: + c = l.next() + switch { + default: + goto yyrule74 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate279: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'C' || c == 'c': + goto yystate280 + case c >= '0' && c <= '9' || c == 'A' || c == 'B' || c >= 'D' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': + goto yystate49 + } + +yystate280: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate281 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate281: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate282 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate282: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate283 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate283: + c = l.next() + switch { + default: + goto yyrule67 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate284: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate285 + case c == 'N' || c == 'n': + goto yystate295 + case c == 'P' || c == 'p': + goto yystate300 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'M' || c == 'O' || c >= 'Q' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'm' || c == 'o' || c >= 'q' && c <= 'z': + goto yystate49 + } + +yystate285: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'N' || c == 'n': + goto yystate286 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate49 + } + +yystate286: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate287 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate287: + c = l.next() + switch { + default: + goto yyrule94 + case c == '0' || c == '2' || c == '4' || c == '5' || c == '7' || c == '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + case c == '1': + goto yystate288 + case c == '3': + goto yystate290 + case c == '6': + goto yystate292 + case c == '8': + goto yystate294 + } + +yystate288: + c = l.next() + switch { + default: + goto yyrule99 + case c == '6': + goto yystate289 + case c >= '0' && c <= '5' || c >= '7' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate289: + c = l.next() + switch { + default: + goto yyrule95 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate290: + c = l.next() + switch { + default: + goto yyrule99 + case c == '0' || c == '1' || c >= '3' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + case c == '2': + goto yystate291 + } + +yystate291: + c = l.next() + switch { + default: + goto yyrule96 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate292: + c = l.next() + switch { + default: + goto yyrule99 + case c == '4': + goto yystate293 + case c >= '0' && c <= '3' || c >= '5' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate293: + c = l.next() + switch { + default: + goto yyrule97 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate294: + c = l.next() + switch { + default: + goto yyrule98 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate295: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'I' || c == 'i': + goto yystate296 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate49 + } + +yystate296: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'Q' || c == 'q': + goto yystate297 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'P' || c >= 'R' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'p' || c >= 'r' && c <= 'z': + goto yystate49 + } + +yystate297: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'U' || c == 'u': + goto yystate298 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate298: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate299 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate299: + c = l.next() + switch { + default: + goto yyrule69 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate300: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'D' || c == 'd': + goto yystate301 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'z': + goto yystate49 + } + +yystate301: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate302 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate302: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'T' || c == 't': + goto yystate303 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate49 + } + +yystate303: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate304 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate304: + c = l.next() + switch { + default: + goto yyrule68 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate305: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'A' || c == 'a': + goto yystate306 + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate49 + } + +yystate306: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'L' || c == 'l': + goto yystate307 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'K' || c >= 'M' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'z': + goto yystate49 + } + +yystate307: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'U' || c == 'u': + goto yystate308 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': + goto yystate49 + } + +yystate308: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate309 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate309: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'S' || c == 's': + goto yystate310 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': + goto yystate49 + } + +yystate310: + c = l.next() + switch { + default: + goto yyrule70 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate311: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'H' || c == 'h': + goto yystate312 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'G' || c >= 'I' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'g' || c >= 'i' && c <= 'z': + goto yystate49 + } + +yystate312: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate313 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate313: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'R' || c == 'r': + goto yystate314 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate49 + } + +yystate314: + c = l.next() + switch { + default: + goto yyrule99 + case c == 'E' || c == 'e': + goto yystate315 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate49 + } + +yystate315: + c = l.next() + switch { + default: + goto yyrule71 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate49 + } + +yystate316: + c = l.next() + goto yyrule11 + +yystate317: + c = l.next() + switch { + default: + goto yyrule101 + case c == '|': + goto yystate318 + } + +yystate318: + c = l.next() + goto yyrule22 + + goto yystate319 // silence unused label error +yystate319: + c = l.next() +yystart319: + switch { + default: + goto yystate320 // c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ' + case c == '"': + goto yystate321 + case c == '\\': + goto yystate322 + case c == '\x00': + goto yystate2 + } + +yystate320: + c = l.next() + switch { + default: + goto yyabort + case c == '"': + goto yystate321 + case c == '\\': + goto yystate322 + case c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate320 + } + +yystate321: + c = l.next() + goto yyrule13 + +yystate322: + c = l.next() + switch { + default: + goto yyabort + case c == '"': + goto yystate323 + case c == '\\': + goto yystate322 + case c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate320 + } + +yystate323: + c = l.next() + switch { + default: + goto yyrule13 + case c == '"': + goto yystate321 + case c == '\\': + goto yystate322 + case c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate320 + } + + goto yystate324 // silence unused label error +yystate324: + c = l.next() +yystart324: + switch { + default: + goto yystate325 // c >= '\x01' && c <= '_' || c >= 'a' && c <= 'ÿ' + case c == '\x00': + goto yystate2 + case c == '`': + goto yystate326 + } + +yystate325: + c = l.next() + switch { + default: + goto yyabort + case c == '`': + goto yystate326 + case c >= '\x01' && c <= '_' || c >= 'a' && c <= 'ÿ': + goto yystate325 + } + +yystate326: + c = l.next() + goto yyrule14 + +yyrule1: // \0 + { + return 0 + } +yyrule2: // [ \t\n\r]+ + + goto yystate0 +yyrule3: // --.* + + goto yystate0 +yyrule4: // \/\/.* + + goto yystate0 +yyrule5: // \/\*([^*]|\*+[^*/])*\*+\/ + + goto yystate0 +yyrule6: // {imaginary_ilit} + { + return l.int(lval, true) + } +yyrule7: // {imaginary_lit} + { + return l.float(lval, true) + } +yyrule8: // {int_lit} + { + return l.int(lval, false) + } +yyrule9: // {float_lit} + { + return l.float(lval, false) + } +yyrule10: // \" + { + l.sc = S1 + goto yystate0 + } +yyrule11: // ` + { + l.sc = S2 + goto yystate0 + } +yyrule12: // '(\\.|[^'])*' + { + if ret := l.str(lval, ""); ret != stringLit { + return ret + } + lval.item = idealRune(lval.item.(string)[0]) + return intLit + } +yyrule13: // (\\.|[^\"])*\" + { + return l.str(lval, "\"") + } +yyrule14: // ([^`]|\n)*` + { + return l.str(lval, "`") + } +yyrule15: // "&&" + { + return andand + } +yyrule16: // "&^" + { + return andnot + } +yyrule17: // "<<" + { + return lsh + } +yyrule18: // "<=" + { + return le + } +yyrule19: // "==" + { + return eq + } +yyrule20: // ">=" + { + return ge + } +yyrule21: // "!=" + { + return neq + } +yyrule22: // "||" + { + return oror + } +yyrule23: // ">>" + { + return rsh + } +yyrule24: // {add} + { + return add + } +yyrule25: // {alter} + { + return alter + } +yyrule26: // {and} + { + return and + } +yyrule27: // {asc} + { + return asc + } +yyrule28: // {as} + { + return as + } +yyrule29: // {begin} + { + return begin + } +yyrule30: // {between} + { + return between + } +yyrule31: // {by} + { + return by + } +yyrule32: // {column} + { + return column + } +yyrule33: // {commit} + { + return commit + } +yyrule34: // {create} + { + return create + } +yyrule35: // {default} + { + return defaultKwd + } +yyrule36: // {delete} + { + return deleteKwd + } +yyrule37: // {desc} + { + return desc + } +yyrule38: // {distinct} + { + return distinct + } +yyrule39: // {drop} + { + return drop + } +yyrule40: // {exists} + { + return exists + } +yyrule41: // {explain} + { + return explain + } +yyrule42: // {from} + { + return from + } +yyrule43: // {full} + { + return full + } +yyrule44: // {group} + { + return group + } +yyrule45: // {if} + { + return ifKwd + } +yyrule46: // {index} + { + return index + } +yyrule47: // {insert} + { + return insert + } +yyrule48: // {into} + { + return into + } +yyrule49: // {in} + { + return in + } +yyrule50: // {is} + { + return is + } +yyrule51: // {join} + { + return join + } +yyrule52: // {left} + { + return left + } +yyrule53: // {like} + { + return like + } +yyrule54: // {limit} + { + return limit + } +yyrule55: // {not} + { + return not + } +yyrule56: // {offset} + { + return offset + } +yyrule57: // {on} + { + return on + } +yyrule58: // {order} + { + return order + } +yyrule59: // {or} + { + return or + } +yyrule60: // {outer} + { + return outer + } +yyrule61: // {right} + { + return right + } +yyrule62: // {rollback} + { + return rollback + } +yyrule63: // {select} + { + l.agg = append(l.agg, false) + return selectKwd + } +yyrule64: // {set} + { + return set + } +yyrule65: // {table} + { + return tableKwd + } +yyrule66: // {transaction} + { + return transaction + } +yyrule67: // {truncate} + { + return truncate + } +yyrule68: // {update} + { + return update + } +yyrule69: // {unique} + { + return unique + } +yyrule70: // {values} + { + return values + } +yyrule71: // {where} + { + return where + } +yyrule72: // {null} + { + lval.item = nil + return null + } +yyrule73: // {false} + { + lval.item = false + return falseKwd + } +yyrule74: // {true} + { + lval.item = true + return trueKwd + } +yyrule75: // {bigint} + { + lval.item = qBigInt + return bigIntType + } +yyrule76: // {bigrat} + { + lval.item = qBigRat + return bigRatType + } +yyrule77: // {blob} + { + lval.item = qBlob + return blobType + } +yyrule78: // {bool} + { + lval.item = qBool + return boolType + } +yyrule79: // {byte} + { + lval.item = qUint8 + return byteType + } +yyrule80: // {complex}128 + { + lval.item = qComplex128 + return complex128Type + } +yyrule81: // {complex}64 + { + lval.item = qComplex64 + return complex64Type + } +yyrule82: // {duration} + { + lval.item = qDuration + return durationType + } +yyrule83: // {float} + { + lval.item = qFloat64 + return floatType + } +yyrule84: // {float}32 + { + lval.item = qFloat32 + return float32Type + } +yyrule85: // {float}64 + { + lval.item = qFloat64 + return float64Type + } +yyrule86: // {int} + { + lval.item = qInt64 + return intType + } +yyrule87: // {int}16 + { + lval.item = qInt16 + return int16Type + } +yyrule88: // {int}32 + { + lval.item = qInt32 + return int32Type + } +yyrule89: // {int}64 + { + lval.item = qInt64 + return int64Type + } +yyrule90: // {int}8 + { + lval.item = qInt8 + return int8Type + } +yyrule91: // {rune} + { + lval.item = qInt32 + return runeType + } +yyrule92: // {string} + { + lval.item = qString + return stringType + } +yyrule93: // {time} + { + lval.item = qTime + return timeType + } +yyrule94: // {uint} + { + lval.item = qUint64 + return uintType + } +yyrule95: // {uint}16 + { + lval.item = qUint16 + return uint16Type + } +yyrule96: // {uint}32 + { + lval.item = qUint32 + return uint32Type + } +yyrule97: // {uint}64 + { + lval.item = qUint64 + return uint64Type + } +yyrule98: // {uint}8 + { + lval.item = qUint8 + return uint8Type + } +yyrule99: // {ident} + { + lval.item = string(l.val) + return identifier + } +yyrule100: // ($|\?){D} + { + lval.item, _ = strconv.Atoi(string(l.val[1:])) + return qlParam + } +yyrule101: // . + { + return c0 + } + panic("unreachable") + + goto yyabort // silence unused label error + +yyabort: // no lexem recognized + return int(unicode.ReplacementChar) +} + +func (l *lexer) npos() (line, col int) { + if line, col = l.nline, l.ncol; col == 0 { + line-- + col = l.lcol + 1 + } + return +} + +func (l *lexer) str(lval *yySymType, pref string) int { + l.sc = 0 + s := pref + string(l.val) + s, err := strconv.Unquote(s) + if err != nil { + l.err("string literal: %v", err) + return int(unicode.ReplacementChar) + } + + lval.item = s + return stringLit +} + +func (l *lexer) int(lval *yySymType, im bool) int { + if im { + l.val = l.val[:len(l.val)-1] + } + n, err := strconv.ParseUint(string(l.val), 0, 64) + if err != nil { + l.err("integer literal: %v", err) + return int(unicode.ReplacementChar) + } + + if im { + lval.item = idealComplex(complex(0, float64(n))) + return imaginaryLit + } + + switch { + case n < math.MaxInt64: + lval.item = idealInt(n) + default: + lval.item = idealUint(n) + } + return intLit +} + +func (l *lexer) float(lval *yySymType, im bool) int { + if im { + l.val = l.val[:len(l.val)-1] + } + n, err := strconv.ParseFloat(string(l.val), 64) + if err != nil { + l.err("float literal: %v", err) + return int(unicode.ReplacementChar) + } + + if im { + lval.item = idealComplex(complex(0, n)) + return imaginaryLit + } + + lval.item = idealFloat(n) + return floatLit +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/scanner.l b/Godeps/_workspace/src/github.com/cznic/ql/scanner.l new file mode 100644 index 000000000..1f2871418 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/scanner.l @@ -0,0 +1,462 @@ +/* +Copyright (c) 2014 The ql Authors. All rights reserved. +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file. +*/ + +%{ + +package ql + +import ( + "fmt" + "math" + "strconv" + "unicode" +) + +type lexer struct { + agg []bool + c int + col int + errs []error + expr expression + i int + inj int + lcol int + line int + list []stmt + ncol int + nline int + params int + sc int + src string + val []byte + root bool +} + +func newLexer(src string) (l *lexer) { + l = &lexer{ + src: src, + nline: 1, + ncol: 0, + } + l.next() + return +} + +func (l *lexer) next() int { + if l.c != 0 { + l.val = append(l.val, byte(l.c)) + } + l.c = 0 + if l.i < len(l.src) { + l.c = int(l.src[l.i]) + l.i++ + } + switch l.c { + case '\n': + l.lcol = l.ncol + l.nline++ + l.ncol = 0 + default: + l.ncol++ + } + return l.c +} + +func (l *lexer) err0(ln, c int, s string, arg ...interface{}) { + err := fmt.Errorf(fmt.Sprintf("%d:%d ", ln, c)+s, arg...) + l.errs = append(l.errs, err) +} + +func (l *lexer) err(s string, arg ...interface{}) { + l.err0(l.line, l.col, s, arg...) +} + +func (l *lexer) Error(s string) { + l.err(s) +} + +func (l *lexer) Lex(lval *yySymType) (r int) { + //defer func() { dbg("Lex -> %d(%#x)", r, r) }() + defer func() { + lval.line, lval.col = l.line, l.col + }() + const ( + INITIAL = iota + S1 + S2 + ) + + if n := l.inj; n != 0 { + l.inj = 0 + return n + } + + c0, c := 0, l.c +%} + +int_lit {decimal_lit}|{octal_lit}|{hex_lit} +decimal_lit [1-9][0-9]* +octal_lit 0[0-7]* +hex_lit 0[xX][0-9a-fA-F]+ + +float_lit {D}"."{D}?{E}?|{D}{E}|"."{D}{E}? +D [0-9]+ +E [eE][-+]?[0-9]+ + +imaginary_ilit {D}i +imaginary_lit {float_lit}i + +a [aA] +b [bB] +c [cC] +d [dD] +e [eE] +f [fF] +g [gG] +h [hH] +i [iI] +j [jJ] +k [kK] +l [lL] +m [mM] +n [nN] +o [oO] +p [pP] +q [qQ] +r [rR] +s [sS] +t [tT] +u [uU] +v [vV] +w [wW] +x [xX] +y [yY] +z [zZ] + +add {a}{d}{d} +alter {a}{l}{t}{e}{r} +and {a}{n}{d} +as {a}{s} +asc {a}{s}{c} +begin {b}{e}{g}{i}{n} +between {b}{e}{t}{w}{e}{e}{n} +by {b}{y} +column {c}{o}{l}{u}{m}{n} +commit {c}{o}{m}{m}{i}{t} +create {c}{r}{e}{a}{t}{e} +default {d}{e}{f}{a}{u}{l}{t} +delete {d}{e}{l}{e}{t}{e} +desc {d}{e}{s}{c} +distinct {d}{i}{s}{t}{i}{n}{c}{t} +drop {d}{r}{o}{p} +exists {e}{x}{i}{s}{t}{s} +explain {e}{x}{p}{l}{a}{i}{n} +from {f}{r}{o}{m} +full {f}{u}{l}{l} +group {g}{r}{o}{u}{p} +if {i}{f} +in {i}{n} +index {i}{n}{d}{e}{x} +insert {i}{n}{s}{e}{r}{t} +into {i}{n}{t}{o} +is {i}{s} +join {j}{o}{i}{n} +left {l}{e}{f}{t} +like {l}{i}{k}{e} +limit {l}{i}{m}{i}{t} +not {n}{o}{t} +offset {o}{f}{f}{s}{e}{t} +on {o}{n} +or {o}{r} +order {o}{r}{d}{e}{r} +outer {o}{u}{t}{e}{r} +right {r}{i}{g}{h}{t} +rollback {r}{o}{l}{l}{b}{a}{c}{k} +select {s}{e}{l}{e}{c}{t} +set {s}{e}{t} +table {t}{a}{b}{l}{e} +transaction {t}{r}{a}{n}{s}{a}{c}{t}{i}{o}{n} +truncate {t}{r}{u}{n}{c}{a}{t}{e} +unique {u}{n}{i}{q}{u}{e} +update {u}{p}{d}{a}{t}{e} +values {v}{a}{l}{u}{e}{s} +where {w}{h}{e}{r}{e} + +null {n}{u}{l}{l} +false {f}{a}{l}{s}{e} +true {t}{r}{u}{e} + +bigint {b}{i}{g}{i}{n}{t} +bigrat {b}{i}{g}{r}{a}{t} +blob {b}{l}{o}{b} +bool {b}{o}{o}{l} +byte {b}{y}{t}{e} +complex {c}{o}{m}{p}{l}{e}{x} +duration {d}{u}{r}{a}{t}{i}{o}{n} +float {f}{l}{o}{a}{t} +int {i}{n}{t} +rune {r}{u}{n}{e} +string {s}{t}{r}{i}{n}{g} +time {t}{i}{m}{e} +uint {u}{i}{n}{t} + +idchar0 [a-zA-Z_] +idchars {idchar0}|[0-9] +ident {idchar0}{idchars}* + +%yyc c +%yyn c = l.next() +%yyt l.sc + +%x S1 S2 + +%% + l.val = l.val[:0] + c0, l.line, l.col = l.c, l.nline, l.ncol + +<*>\0 return 0 + +[ \t\n\r]+ +--.* +\/\/.* +\/\*([^*]|\*+[^*/])*\*+\/ + +{imaginary_ilit} return l.int(lval, true) +{imaginary_lit} return l.float(lval, true) +{int_lit} return l.int(lval, false) +{float_lit} return l.float(lval, false) + +\" l.sc = S1 +` l.sc = S2 + +'(\\.|[^'])*' if ret := l.str(lval, ""); ret != stringLit { + return ret + } + lval.item = idealRune(lval.item.(string)[0]) + return intLit + +(\\.|[^\"])*\" return l.str(lval, "\"") +([^`]|\n)*` return l.str(lval, "`") + +"&&" return andand +"&^" return andnot +"<<" return lsh +"<=" return le +"==" return eq +">=" return ge +"!=" return neq +"||" return oror +">>" return rsh + +{add} return add +{alter} return alter +{and} return and +{asc} return asc +{as} return as +{begin} return begin +{between} return between +{by} return by +{column} return column +{commit} return commit +{create} return create +{default} return defaultKwd +{delete} return deleteKwd +{desc} return desc +{distinct} return distinct +{drop} return drop +{exists} return exists +{explain} return explain +{from} return from +{full} return full +{group} return group +{if} return ifKwd +{index} return index +{insert} return insert +{into} return into +{in} return in +{is} return is +{join} return join +{left} return left +{like} return like +{limit} return limit +{not} return not +{offset} return offset +{on} return on +{order} return order +{or} return or +{outer} return outer +{right} return right + +{rollback} return rollback + +{select} l.agg = append(l.agg, false) + return selectKwd + +{set} return set +{table} return tableKwd +{transaction} return transaction +{truncate} return truncate +{update} return update +{unique} return unique +{values} return values +{where} return where + +{null} lval.item = nil + return null + +{false} lval.item = false + return falseKwd + +{true} lval.item = true + return trueKwd + +{bigint} lval.item = qBigInt + return bigIntType + +{bigrat} lval.item = qBigRat + return bigRatType + +{blob} lval.item = qBlob + return blobType + +{bool} lval.item = qBool + return boolType + +{byte} lval.item = qUint8 + return byteType + +{complex}128 lval.item = qComplex128 + return complex128Type + +{complex}64 lval.item = qComplex64 + return complex64Type + +{duration} lval.item = qDuration + return durationType + +{float} lval.item = qFloat64 + return floatType + +{float}32 lval.item = qFloat32 + return float32Type + +{float}64 lval.item = qFloat64 + return float64Type + +{int} lval.item = qInt64 + return intType + +{int}16 lval.item = qInt16 + return int16Type + +{int}32 lval.item = qInt32 + return int32Type + +{int}64 lval.item = qInt64 + return int64Type + +{int}8 lval.item = qInt8 + return int8Type + +{rune} lval.item = qInt32 + return runeType + +{string} lval.item = qString + return stringType + +{time} lval.item = qTime + return timeType + +{uint} lval.item = qUint64 + return uintType + +{uint}16 lval.item = qUint16 + return uint16Type + +{uint}32 lval.item = qUint32 + return uint32Type + +{uint}64 lval.item = qUint64 + return uint64Type + +{uint}8 lval.item = qUint8 + return uint8Type + +{ident} lval.item = string(l.val) + return identifier + +($|\?){D} lval.item, _ = strconv.Atoi(string(l.val[1:])) + return qlParam + +. return c0 + +%% + return int(unicode.ReplacementChar) +} + +func (l *lexer) npos() (line, col int) { + if line, col = l.nline, l.ncol; col == 0 { + line-- + col = l.lcol+1 + } + return +} + +func (l *lexer) str(lval *yySymType, pref string) int { + l.sc = 0 + s := pref + string(l.val) + s, err := strconv.Unquote(s) + if err != nil { + l.err("string literal: %v", err) + return int(unicode.ReplacementChar) + } + + lval.item = s + return stringLit +} + +func (l *lexer) int(lval *yySymType, im bool) int { + if im { + l.val = l.val[:len(l.val)-1] + } + n, err := strconv.ParseUint(string(l.val), 0, 64) + if err != nil { + l.err("integer literal: %v", err) + return int(unicode.ReplacementChar) + } + + if im { + lval.item = idealComplex(complex(0, float64(n))) + return imaginaryLit + } + + switch { + case n < math.MaxInt64: + lval.item = idealInt(n) + default: + lval.item = idealUint(n) + } + return intLit +} + +func (l *lexer) float(lval *yySymType, im bool) int { + if im { + l.val = l.val[:len(l.val)-1] + } + n, err := strconv.ParseFloat(string(l.val), 64) + if err != nil { + l.err("float literal: %v", err) + return int(unicode.ReplacementChar) + } + + if im { + lval.item = idealComplex(complex(0, n)) + return imaginaryLit + } + + lval.item = idealFloat(n) + return floatLit +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/scanner_test.go b/Godeps/_workspace/src/github.com/cznic/ql/scanner_test.go new file mode 100644 index 000000000..150c902a6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/scanner_test.go @@ -0,0 +1,86 @@ +// Copyright (c) 2014 ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "fmt" + "testing" + "unicode" +) + +var bad = int(unicode.ReplacementChar) + +func tok2name(i int) string { + if i == unicode.ReplacementChar { + return "" + } + + if i < 128 { + return fmt.Sprintf("tok-'%c'", i) + } + + return fmt.Sprintf("tok-%d", i) +} + +func TestScaner0(t *testing.T) { + table := []struct { + src string + tok, line, col, nline, ncol int + val string + }{ + {"a", identifier, 1, 1, 1, 2, "a"}, + {" a", identifier, 1, 2, 1, 3, "a"}, + {"a ", identifier, 1, 1, 1, 2, "a"}, + {" a ", identifier, 1, 2, 1, 3, "a"}, + {"\na", identifier, 2, 1, 2, 2, "a"}, + + {"a\n", identifier, 1, 1, 1, 2, "a"}, + {"\na\n", identifier, 2, 1, 2, 2, "a"}, + {"\n a", identifier, 2, 2, 2, 3, "a"}, + {"a \n", identifier, 1, 1, 1, 2, "a"}, + {"\n a \n", identifier, 2, 2, 2, 3, "a"}, + + {"ab", identifier, 1, 1, 1, 3, "ab"}, + {" ab", identifier, 1, 2, 1, 4, "ab"}, + {"ab ", identifier, 1, 1, 1, 3, "ab"}, + {" ab ", identifier, 1, 2, 1, 4, "ab"}, + {"\nab", identifier, 2, 1, 2, 3, "ab"}, + + {"ab\n", identifier, 1, 1, 1, 3, "ab"}, + {"\nab\n", identifier, 2, 1, 2, 3, "ab"}, + {"\n ab", identifier, 2, 2, 2, 4, "ab"}, + {"ab \n", identifier, 1, 1, 1, 3, "ab"}, + {"\n ab \n", identifier, 2, 2, 2, 4, "ab"}, + + {"c", identifier, 1, 1, 1, 2, "c"}, + {"cR", identifier, 1, 1, 1, 3, "cR"}, + {"cRe", identifier, 1, 1, 1, 4, "cRe"}, + {"cReA", identifier, 1, 1, 1, 5, "cReA"}, + {"cReAt", identifier, 1, 1, 1, 6, "cReAt"}, + + {"cReATe", create, 1, 1, 1, 7, "cReATe"}, + {"cReATeD", identifier, 1, 1, 1, 8, "cReATeD"}, + {"2", intLit, 1, 1, 1, 2, "2"}, + {"2.", floatLit, 1, 1, 1, 3, "2."}, + {"2.3", floatLit, 1, 1, 1, 4, "2.3"}, + } + + lval := &yySymType{} + for i, test := range table { + l := newLexer(test.src) + tok := l.Lex(lval) + nline, ncol := l.npos() + val := string(l.val) + if tok != test.tok || l.line != test.line || l.col != test.col || + nline != test.nline || ncol != test.ncol || + val != test.val { + t.Fatalf( + "%d g: %s %d:%d-%d:%d %q, e: %s %d:%d-%d:%d %q", + i, tok2name(tok), l.line, l.col, nline, ncol, val, + tok2name(test.tok), test.line, test.col, test.nline, test.ncol, test.val, + ) + } + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/stmt.go b/Godeps/_workspace/src/github.com/cznic/ql/stmt.go new file mode 100644 index 000000000..8de367d4c --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/stmt.go @@ -0,0 +1,1268 @@ +// Copyright (c) 2014 ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "bytes" + "fmt" + "strings" + "sync" + + "github.com/cznic/strutil" +) + +// NOTE: all stmt implementations must be safe for concurrent use by multiple +// goroutines. If the exec method requires any execution domain local data, +// they must be held out of the implementing instance. +var ( + _ stmt = (*alterTableAddStmt)(nil) + _ stmt = (*alterTableDropColumnStmt)(nil) + _ stmt = (*createIndexStmt)(nil) + _ stmt = (*createTableStmt)(nil) + _ stmt = (*deleteStmt)(nil) //TODO optimizer plan + _ stmt = (*dropIndexStmt)(nil) + _ stmt = (*dropTableStmt)(nil) + _ stmt = (*explainStmt)(nil) + _ stmt = (*insertIntoStmt)(nil) + _ stmt = (*selectStmt)(nil) + _ stmt = (*truncateTableStmt)(nil) + _ stmt = (*updateStmt)(nil) //TODO optimizer plan + _ stmt = beginTransactionStmt{} + _ stmt = commitStmt{} + _ stmt = rollbackStmt{} +) + +var ( + createColumn2 = mustCompile(` + create table if not exists __Column2 ( + TableName string, + Name string, + NotNull bool, + ConstraintExpr string, + DefaultExpr string, + ); + create index if not exists __Column2TableName on __Column2(TableName); + `) + + insertColumn2 = mustCompile(`insert into __Column2 values($1, $2, $3, $4, $5)`) + + selectColumn2 = MustCompile(` + select Name, NotNull, ConstraintExpr, DefaultExpr + from __Column2 + where TableName == $1 + `) + + deleteColumn2 = mustCompile(` + delete from __Column2 + where TableName == $1 && Name == $2 + `) + + createIndex2 = mustCompile(` + // Index register 2. + create table if not exists __Index2( + TableName string, + IndexName string, + IsUnique bool, + IsSimple bool, // Just a column name or id(). + Root int64, // BTree handle + ); + + // Expressions for given index. Compared in order of id(__Index2_Expr). + create table if not exists __Index2_Expr( + Index2_ID int, + Expr string, + ); + + create index if not exists __xIndex2_TableName on __Index2(TableName); + create unique index if not exists __xIndex2_IndexName on __Index2(IndexName); + create index if not exists __xIndex2_ID on __Index2(id()); + create index if not exists __xIndex2_Expr_Index2_ID on __Index2_Expr(Index2_ID); +`) + + insertIndex2 = mustCompile("insert into __Index2 values($1, $2, $3, $4, $5)") + insertIndex2Expr = mustCompile("insert into __Index2_Expr values($1, $2)") + + deleteIndex2ByIndexName = mustCompile(` + delete from __Index2_Expr + where Index2_ID in ( + select id() from __Index2 where IndexName == $1; + ); + + delete from __Index2 + where IndexName == $1; +`) + deleteIndex2ByTableName = mustCompile(` + delete from __Index2_Expr + where Index2_ID in ( + select id() from __Index2 where TableName == $1; + ); + + delete from __Index2 + where TableName == $1; +`) +) + +type stmt interface { + // never invoked for + // - beginTransactionStmt + // - commitStmt + // - rollbackStmt + exec(ctx *execCtx) (Recordset, error) + + explain(ctx *execCtx, w strutil.Formatter) + + // return value ignored for + // - beginTransactionStmt + // - commitStmt + // - rollbackStmt + isUpdating() bool + String() string +} + +type execCtx struct { //LATER +shared temp + db *DB + arg []interface{} +} + +type explainStmt struct { + s stmt +} + +func (s *explainStmt) explain(ctx *execCtx, w strutil.Formatter) { + for { + x, ok := s.s.(*explainStmt) + if !ok { + s.s.explain(ctx, w) + return + } + + s = x + } +} + +func (s *explainStmt) String() string { + return "EXPLAIN " + s.s.String() +} + +func (*explainStmt) isUpdating() bool { return false } + +func (s *explainStmt) exec(ctx *execCtx) (_ Recordset, err error) { + return recordset{ctx, &explainDefaultPlan{s.s}, ctx.db.cc}, nil +} + +type updateStmt struct { + tableName string + list []assignment + where expression +} + +func (s *updateStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *updateStmt) String() string { + u := fmt.Sprintf("UPDATE %s", s.tableName) + a := make([]string, len(s.list)) + for i, v := range s.list { + a[i] = v.String() + } + w := "" + if s.where != nil { + w = fmt.Sprintf(" WHERE %s", s.where) + } + return fmt.Sprintf("%s %s%s;", u, strings.Join(a, ", "), w) +} + +func (s *updateStmt) exec(ctx *execCtx) (_ Recordset, err error) { + t, ok := ctx.db.root.tables[s.tableName] + if !ok { + return nil, fmt.Errorf("UPDATE: table %s does not exist", s.tableName) + } + + tcols := make([]*col, len(s.list)) + for i, asgn := range s.list { + col := findCol(t.cols, asgn.colName) + if col == nil { + return nil, fmt.Errorf("UPDATE: unknown column %s", asgn.colName) + } + tcols[i] = col + } + + m := map[interface{}]interface{}{} + var nh int64 + expr := s.where + blobCols := t.blobCols() + cc := ctx.db.cc + var old []interface{} + var touched []bool + if t.hasIndices() { + old = make([]interface{}, len(t.cols0)) + touched = make([]bool, len(t.cols0)) + } + for h := t.head; h != 0; h = nh { + // Read can return lazily expanded chunks + data, err := t.store.Read(nil, h, t.cols...) + if err != nil { + return nil, err + } + + nh = data[0].(int64) + for _, col := range t.cols { + m[col.name] = data[2+col.index] + } + id := data[1].(int64) + m["$id"] = id + if expr != nil { + val, err := s.where.eval(ctx, m) + if err != nil { + return nil, err + } + + if val == nil { + continue + } + + x, ok := val.(bool) + if !ok { + return nil, fmt.Errorf("invalid WHERE expression %s (value of type %T)", val, val) + } + + if !x { + continue + } + } + + // hit + for _, ix := range t.indices2 { + vlist, err := ix.eval(ctx, t.cols, id, data[2:]) + if err != nil { + return nil, err + } + + if err := ix.x.Delete(vlist, h); err != nil { + return nil, err + } + } + for i, asgn := range s.list { + val, err := asgn.expr.eval(ctx, m) + if err != nil { + return nil, err + } + + colIndex := tcols[i].index + if t.hasIndices() { + old[colIndex] = data[2+colIndex] + touched[colIndex] = true + } + data[2+colIndex] = val + } + if err = typeCheck(data[2:], t.cols); err != nil { + return nil, err + } + + if err = t.checkConstraintsAndDefaults(ctx, data[2:], m); err != nil { + return nil, err + } + + for i, v := range t.indices { + if i == 0 { // id() N/A + continue + } + + if v == nil || !touched[i-1] { + continue + } + + if err = v.x.Delete([]interface{}{old[i-1]}, h); err != nil { + return nil, err + } + } + + if err = t.store.UpdateRow(h, blobCols, data...); err != nil { //LATER detect which blobs are actually affected + return nil, err + } + + for i, v := range t.indices { + if i == 0 { // id() N/A + continue + } + + if v == nil || !touched[i-1] { + continue + } + + if err = v.x.Create([]interface{}{data[2+i-1]}, h); err != nil { + return nil, err + } + } + for _, ix := range t.indices2 { + vlist, err := ix.eval(ctx, t.cols, id, data[2:]) + if err != nil { + return nil, err + } + + if err := ix.x.Create(vlist, h); err != nil { + return nil, err + } + } + + cc.RowsAffected++ + } + return +} + +func (s *updateStmt) isUpdating() bool { return true } + +type deleteStmt struct { + tableName string + where expression +} + +func (s *deleteStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *deleteStmt) String() string { + switch { + case s.where == nil: + return fmt.Sprintf("DELETE FROM %s;", s.tableName) + default: + return fmt.Sprintf("DELETE FROM %s WHERE %s;", s.tableName, s.where) + } +} + +func (s *deleteStmt) exec(ctx *execCtx) (_ Recordset, err error) { + t, ok := ctx.db.root.tables[s.tableName] + if !ok { + return nil, fmt.Errorf("DELETE FROM: table %s does not exist", s.tableName) + } + + m := map[interface{}]interface{}{} + var ph, h, nh int64 + var data []interface{} + blobCols := t.blobCols() + cc := ctx.db.cc + for h = t.head; h != 0; ph, h = h, nh { + for i, v := range data { + c, ok := v.(chunk) + if !ok { + continue + } + + data[i] = c.b + } + // Read can return lazily expanded chunks + data, err = t.store.Read(nil, h, t.cols...) + if err != nil { + return nil, err + } + + nh = data[0].(int64) + for _, col := range t.cols { + m[col.name] = data[2+col.index] + } + id := data[1].(int64) + m["$id"] = id + val, err := s.where.eval(ctx, m) + if err != nil { + return nil, err + } + + if val == nil { + continue + } + + x, ok := val.(bool) + if !ok { + return nil, fmt.Errorf("invalid WHERE expression %s (value of type %T)", val, val) + } + + if !x { + continue + } + + // hit + for i, v := range t.indices { + if v == nil { + continue + } + + // overflow chunks left in place + if err = v.x.Delete([]interface{}{data[i+1]}, h); err != nil { + return nil, err + } + } + for _, ix := range t.indices2 { + vlist, err := ix.eval(ctx, t.cols, id, data[2:]) + if err != nil { + return nil, err + } + + if err := ix.x.Delete(vlist, h); err != nil { + return nil, err + } + } + + // overflow chunks freed here + if err = t.store.Delete(h, blobCols...); err != nil { + return nil, err + } + + cc.RowsAffected++ + switch { + case ph == 0 && nh == 0: // "only" + fallthrough + case ph == 0 && nh != 0: // "first" + if err = t.store.Update(t.hhead, nh); err != nil { + return nil, err + } + + t.head, h = nh, 0 + case ph != 0 && nh == 0: // "last" + fallthrough + case ph != 0 && nh != 0: // "inner" + pdata, err := t.store.Read(nil, ph, t.cols...) + if err != nil { + return nil, err + } + + for i, v := range pdata { + if x, ok := v.(chunk); ok { + pdata[i] = x.b + } + } + pdata[0] = nh + if err = t.store.Update(ph, pdata...); err != nil { + return nil, err + } + + h = ph + } + } + + return +} + +func (s *deleteStmt) isUpdating() bool { return true } + +type truncateTableStmt struct { + tableName string +} + +func (s *truncateTableStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *truncateTableStmt) String() string { return fmt.Sprintf("TRUNCATE TABLE %s;", s.tableName) } + +func (s *truncateTableStmt) exec(ctx *execCtx) (Recordset, error) { + t, ok := ctx.db.root.tables[s.tableName] + if !ok { + return nil, fmt.Errorf("TRUNCATE TABLE: table %s does not exist", s.tableName) + } + + return nil, t.truncate() +} + +func (s *truncateTableStmt) isUpdating() bool { return true } + +type dropIndexStmt struct { + ifExists bool + indexName string +} + +func (s *dropIndexStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *dropIndexStmt) String() string { return fmt.Sprintf("DROP INDEX %s;", s.indexName) } + +func (s *dropIndexStmt) exec(ctx *execCtx) (Recordset, error) { + t, x := ctx.db.root.findIndexByName(s.indexName) + if x == nil { + if s.ifExists { + return nil, nil + } + + return nil, fmt.Errorf("DROP INDEX: index %s does not exist", s.indexName) + } + + if ctx.db.hasAllIndex2() { + if err := ctx.db.deleteIndex2ByIndexName(s.indexName); err != nil { + return nil, err + } + } + + switch ix := x.(type) { + case *indexedCol: + for i, v := range t.indices { + if v == nil || v.name != s.indexName { + continue + } + + return nil, t.dropIndex(i) + } + case *index2: + delete(t.indices2, s.indexName) + return nil, ix.x.Drop() + } + + panic("internal error 058") +} + +func (s *dropIndexStmt) isUpdating() bool { return true } + +type dropTableStmt struct { + ifExists bool + tableName string +} + +func (s *dropTableStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *dropTableStmt) String() string { return fmt.Sprintf("DROP TABLE %s;", s.tableName) } + +func (s *dropTableStmt) exec(ctx *execCtx) (Recordset, error) { + t, ok := ctx.db.root.tables[s.tableName] + if !ok { + if s.ifExists { + return nil, nil + } + + return nil, fmt.Errorf("DROP TABLE: table %s does not exist", s.tableName) + } + + if ctx.db.hasAllIndex2() { + if err := ctx.db.deleteIndex2ByTableName(s.tableName); err != nil { + return nil, err + } + } + + return nil, ctx.db.root.dropTable(t) +} + +func (s *dropTableStmt) isUpdating() bool { return true } + +type alterTableDropColumnStmt struct { + tableName, colName string +} + +func (s *alterTableDropColumnStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *alterTableDropColumnStmt) String() string { + return fmt.Sprintf("ALTER TABLE %s DROP COLUMN %s;", s.tableName, s.colName) +} + +func (s *alterTableDropColumnStmt) exec(ctx *execCtx) (Recordset, error) { + t, ok := ctx.db.root.tables[s.tableName] + if !ok { + return nil, fmt.Errorf("ALTER TABLE: table %s does not exist", s.tableName) + } + + cols := t.cols + for _, c := range cols { + if c.name == s.colName { + if len(cols) == 1 { + return nil, fmt.Errorf("ALTER TABLE %s DROP COLUMN: cannot drop the only column: %s", s.tableName, s.colName) + } + + if _, ok := ctx.db.root.tables["__Column2"]; ok { + if _, err := deleteColumn2.l[0].exec(&execCtx{db: ctx.db, arg: []interface{}{s.tableName, c.name}}); err != nil { + return nil, err + } + } + + c.name = "" + t.cols0[c.index].name = "" + if t.hasIndices() { + if len(t.indices) != 0 { + if v := t.indices[c.index+1]; v != nil { + if err := t.dropIndex(c.index + 1); err != nil { + return nil, err + } + + if ctx.db.hasAllIndex2() { + if err := ctx.db.deleteIndex2ByIndexName(v.name); err != nil { + return nil, err + } + } + } + } + + for nm, ix := range t.indices2 { + for _, e := range ix.exprList { + m := mentionedColumns(e) + if _, ok := m[s.colName]; ok { + if err := ctx.db.deleteIndex2ByIndexName(nm); err != nil { + return nil, err + } + + if err := ix.x.Drop(); err != nil { + return nil, err + } + + delete(t.indices2, nm) + break + } + } + } + } + if err := t.constraintsAndDefaults(ctx); err != nil { + return nil, err + } + + return nil, t.updated() + } + } + + return nil, fmt.Errorf("ALTER TABLE %s DROP COLUMN: column %s does not exist", s.tableName, s.colName) +} + +func (s *alterTableDropColumnStmt) isUpdating() bool { return true } + +type alterTableAddStmt struct { + tableName string + c *col +} + +func (s *alterTableAddStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *alterTableAddStmt) String() string { + r := fmt.Sprintf("ALTER TABLE %s ADD %s %s", s.tableName, s.c.name, typeStr(s.c.typ)) + c := s.c + if x := c.constraint; x != nil { //TODO add (*col).String() + switch e := x.expr; { + case e != nil: + r += " " + e.String() + default: + r += " NOT NULL" + } + } + if x := c.dflt; x != nil { + r += " DEFAULT " + x.String() + } + return r + ";" +} + +func (s *alterTableAddStmt) exec(ctx *execCtx) (Recordset, error) { + t, ok := ctx.db.root.tables[s.tableName] + if !ok { + return nil, fmt.Errorf("ALTER TABLE: table %s does not exist", s.tableName) + } + + hasRecords := t.head != 0 + c := s.c + if c.constraint != nil && hasRecords { + return nil, fmt.Errorf("ALTER TABLE %s ADD %s: cannot add constrained column to table with existing data", s.tableName, c.name) + } + + cols := t.cols + for _, c := range cols { + nm := c.name + if nm == s.c.name { + return nil, fmt.Errorf("ALTER TABLE %s ADD: column %s exists", s.tableName, nm) + } + } + + if len(t.indices) != 0 { + t.indices = append(t.indices, nil) + t.xroots = append(t.xroots, 0) + if err := t.store.Update(t.hxroots, t.xroots...); err != nil { + return nil, err + } + } + + if c.constraint != nil || c.dflt != nil { + for _, s := range createColumn2.l { + _, err := s.exec(&execCtx{db: ctx.db}) + if err != nil { + return nil, err + } + } + notNull := c.constraint != nil && c.constraint.expr == nil + var co, d string + if c.constraint != nil && c.constraint.expr != nil { + co = c.constraint.expr.String() + } + if e := c.dflt; e != nil { + d = e.String() + } + if _, err := insertColumn2.l[0].exec(&execCtx{db: ctx.db, arg: []interface{}{s.tableName, c.name, notNull, co, d}}); err != nil { + return nil, err + } + } + + t.cols0 = append(t.cols0, s.c) + if err := t.constraintsAndDefaults(ctx); err != nil { + return nil, err + } + + return nil, t.updated() +} + +func (s *alterTableAddStmt) isUpdating() bool { return true } + +type selectStmt struct { + distinct bool + flds []*fld + from *joinRset + group *groupByRset + hasAggregates bool + limit *limitRset + mu sync.Mutex + offset *offsetRset + order *orderByRset + where *whereRset +} + +func (s *selectStmt) explain(ctx *execCtx, w strutil.Formatter) { + p, err := s.plan(ctx) + if err != nil { + w.Format("ERROR: %v\n", err) + return + } + + p.explain(w) +} + +func (s *selectStmt) String() string { + var b bytes.Buffer + b.WriteString("SELECT") + if s.distinct { + b.WriteString(" DISTINCT") + } + switch { + case len(s.flds) == 0: + b.WriteString(" *") + default: + a := make([]string, len(s.flds)) + for i, v := range s.flds { + s := v.expr.String() + if v.name != "" && v.name != s { + s += " AS " + v.name + } + a[i] = s + } + b.WriteString(" " + strings.Join(a, ", ")) + } + b.WriteString(" FROM ") + b.WriteString(s.from.String()) + if s.where != nil { + b.WriteString(" WHERE ") + b.WriteString(s.where.expr.String()) + } + if s.group != nil { + b.WriteString(" GROUP BY ") + b.WriteString(strings.Join(s.group.colNames, ", ")) + } + if s.order != nil { + b.WriteString(" ORDER BY ") + b.WriteString(s.order.String()) + } + if s.limit != nil { + b.WriteString(" LIMIT ") + b.WriteString(s.limit.expr.String()) + } + if s.offset != nil { + b.WriteString(" OFFSET ") + b.WriteString(s.offset.expr.String()) + } + b.WriteRune(';') + return b.String() +} + +func (s *selectStmt) plan(ctx *execCtx) (plan, error) { //LATER overlapping goroutines/pipelines + r, err := s.from.plan(ctx) + if err != nil { + return nil, err + } + + if w := s.where; w != nil { + if r, err = (&whereRset{expr: w.expr, src: r}).plan(ctx); err != nil { + return nil, err + } + } + switch { + case !s.hasAggregates && s.group == nil: // nop + case !s.hasAggregates && s.group != nil: + if r, err = (&groupByRset{colNames: s.group.colNames, src: r}).plan(ctx); err != nil { + return nil, err + } + case s.hasAggregates && s.group == nil: + if r, err = (&groupByRset{src: r}).plan(ctx); err != nil { + return nil, err + } + case s.hasAggregates && s.group != nil: + if r, err = (&groupByRset{colNames: s.group.colNames, src: r}).plan(ctx); err != nil { + return nil, err + } + } + if r, err = (&selectRset{flds: s.flds, src: r}).plan(ctx); err != nil { + return nil, err + } + + if s.distinct { + if r, err = (&distinctRset{src: r}).plan(ctx); err != nil { + return nil, err + } + } + if s := s.order; s != nil { + if r, err = (&orderByRset{asc: s.asc, by: s.by, src: r}).plan(ctx); err != nil { + return nil, err + } + } + if s := s.offset; s != nil { + if r, err = (&offsetRset{s.expr, r}).plan(ctx); err != nil { + return nil, err + } + } + if s := s.limit; s != nil { + if r, err = (&limitRset{s.expr, r}).plan(ctx); err != nil { + return nil, err + } + } + return r, nil +} + +func (s *selectStmt) exec(ctx *execCtx) (rs Recordset, err error) { + r, err := s.plan(ctx) + if err != nil { + return nil, err + } + + return recordset{ctx, r, nil}, nil +} + +func (s *selectStmt) isUpdating() bool { return false } + +type insertIntoStmt struct { + colNames []string + lists [][]expression + sel *selectStmt + tableName string +} + +func (s *insertIntoStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *insertIntoStmt) String() string { + cn := "" + if len(s.colNames) != 0 { + cn = fmt.Sprintf(" (%s)", strings.Join(s.colNames, ", ")) + } + switch { + case s.sel != nil: + return fmt.Sprintf("INSERT INTO %s%s %s;", s.tableName, cn, s.sel) + default: + a := make([]string, len(s.lists)) + for i, v := range s.lists { + b := make([]string, len(v)) + for i, v := range v { + b[i] = v.String() + } + a[i] = fmt.Sprintf("(%s)", strings.Join(b, ", ")) + } + return fmt.Sprintf("INSERT INTO %s%s VALUES %s;", s.tableName, cn, strings.Join(a, ", ")) + } +} + +func (s *insertIntoStmt) execSelect(t *table, cols []*col, ctx *execCtx) (_ Recordset, err error) { + //TODO missing rs column number eq check + r, err := s.sel.plan(ctx) + if err != nil { + return nil, err + } + + h := t.head + data0 := make([]interface{}, len(t.cols0)+2) + cc := ctx.db.cc + m := map[interface{}]interface{}{} + if err = r.do(ctx, func(_ interface{}, data []interface{}) (more bool, err error) { + for i, d := range data { + data0[cols[i].index+2] = d + } + if err = typeCheck(data0[2:], cols); err != nil { + return + } + + if err = t.checkConstraintsAndDefaults(ctx, data0[2:], m); err != nil { + return false, err + } + + id, err := t.store.ID() + if err != nil { + return false, err + } + + data0[0] = h + data0[1] = id + + // Any overflow chunks are written here. + if h, err = t.store.Create(data0...); err != nil { + return false, err + } + + for i, v := range t.indices { + if v == nil { + continue + } + + // Any overflow chunks are shared with the BTree key + if err = v.x.Create([]interface{}{data0[i+1]}, h); err != nil { + return false, err + } + } + for _, ix := range t.indices2 { + vlist, err := ix.eval(ctx, t.cols, id, data0[2:]) + if err != nil { + return false, err + } + + if err := ix.x.Create(vlist, h); err != nil { + return false, err + } + } + + cc.RowsAffected++ + ctx.db.root.lastInsertID = id + return true, nil + }); err != nil { + return nil, err + } + + t.head = h + return nil, t.store.Update(t.hhead, h) +} + +func (s *insertIntoStmt) exec(ctx *execCtx) (_ Recordset, err error) { + root := ctx.db.root + t, ok := root.tables[s.tableName] + if !ok { + return nil, fmt.Errorf("INSERT INTO %s: table does not exist", s.tableName) + } + + var cols []*col + switch len(s.colNames) { + case 0: + cols = t.cols + default: + for _, colName := range s.colNames { + if col := findCol(t.cols, colName); col != nil { + cols = append(cols, col) + continue + } + + return nil, fmt.Errorf("INSERT INTO %s: unknown column %s", s.tableName, colName) + } + } + + if s.sel != nil { + return s.execSelect(t, cols, ctx) + } + + for _, list := range s.lists { + if g, e := len(list), len(cols); g != e { + return nil, fmt.Errorf("INSERT INTO %s: expected %d value(s), have %d", s.tableName, e, g) + } + } + + cc := ctx.db.cc + r := make([]interface{}, len(t.cols0)) + m := map[interface{}]interface{}{} + for _, list := range s.lists { + for i, expr := range list { + val, err := expr.eval(ctx, m) + if err != nil { + return nil, err + } + + r[cols[i].index] = val + } + if err = typeCheck(r, cols); err != nil { + return nil, err + } + + if err = t.checkConstraintsAndDefaults(ctx, r, m); err != nil { + return nil, err + } + + id, err := t.addRecord(ctx, r) + if err != nil { + return nil, err + } + + cc.RowsAffected++ + root.lastInsertID = id + } + return nil, nil +} + +func (s *insertIntoStmt) isUpdating() bool { return true } + +type beginTransactionStmt struct{} + +func (s beginTransactionStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (beginTransactionStmt) String() string { return "BEGIN TRANSACTION;" } +func (beginTransactionStmt) exec(*execCtx) (Recordset, error) { + panic("internal error 059") +} +func (beginTransactionStmt) isUpdating() bool { + panic("internal error 060") +} + +type commitStmt struct{} + +func (s commitStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (commitStmt) String() string { return "COMMIT;" } +func (commitStmt) exec(*execCtx) (Recordset, error) { + panic("internal error 061") +} +func (commitStmt) isUpdating() bool { + panic("internal error 062") +} + +type rollbackStmt struct{} + +func (s rollbackStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (rollbackStmt) String() string { return "ROLLBACK;" } +func (rollbackStmt) exec(*execCtx) (Recordset, error) { + panic("internal error 063") +} +func (rollbackStmt) isUpdating() bool { + panic("internal error 064") +} + +type createIndexStmt struct { + colName string // alt. "id()" for simple index on id() + ifNotExists bool + indexName string + tableName string + unique bool + exprList []expression +} + +func (s *createIndexStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *createIndexStmt) isSimpleIndex() bool { return s.colName != "" } + +func (s *createIndexStmt) String() string { + u := "" + if s.unique { + u = "UNIQUE " + } + e := "" + if s.ifNotExists { + e = "IF NOT EXISTS " + } + expr := s.colName + if !s.isSimpleIndex() { + var a []string + for _, v := range s.exprList { + a = append(a, v.String()) + } + expr = strings.Join(a, ", ") + } + return fmt.Sprintf("CREATE %sINDEX %s%s ON %s (%s);", u, e, s.indexName, s.tableName, expr) +} + +func (s *createIndexStmt) exec(ctx *execCtx) (Recordset, error) { + root := ctx.db.root + if t, i := root.findIndexByName(s.indexName); i != nil { + if s.ifNotExists { + return nil, nil + } + + return nil, fmt.Errorf("CREATE INDEX: table %s already has an index named %s", t.name, s.indexName) + } + + if root.tables[s.indexName] != nil { + return nil, fmt.Errorf("CREATE INDEX: index name collision with existing table: %s", s.indexName) + } + + t, ok := root.tables[s.tableName] + if !ok { + return nil, fmt.Errorf("CREATE INDEX: table does not exist %s", s.tableName) + } + + if findCol(t.cols, s.indexName) != nil { + return nil, fmt.Errorf("CREATE INDEX: index name collision with existing column: %s", s.indexName) + } + + var h int64 + var err error + switch { + case s.isSimpleIndex(): + colIndex := -1 + if s.colName != "id()" { + c := findCol(t.cols, s.colName) + if c == nil { + return nil, fmt.Errorf("CREATE INDEX: column does not exist: %s", s.colName) + } + + colIndex = c.index + } + + if h, err = t.addIndex(s.unique, s.indexName, colIndex); err != nil { + return nil, fmt.Errorf("CREATE INDEX: %v", err) + } + + if err = t.updated(); err != nil { + return nil, err + } + default: + for _, e := range s.exprList { + m := mentionedColumns(e) + for colName := range m { + c := findCol(t.cols, colName) + if c == nil { + return nil, fmt.Errorf("CREATE INDEX: column does not exist: %s", colName) + } + } + } + if h, err = t.addIndex2(ctx, s.unique, s.indexName, s.exprList); err != nil { + return nil, fmt.Errorf("CREATE INDEX: %v", err) + } + } + + switch ctx.db.hasIndex2 { + case 0: + if err := ctx.db.createIndex2(); err != nil { + return nil, err + } + + if s.isSimpleIndex() { + return nil, nil + } + case 1: + return nil, nil + case 2: + if s.isSimpleIndex() { + return nil, ctx.db.insertIndex2(s.tableName, s.indexName, []string{s.colName}, s.unique, true, h) + } + default: + panic("internal error 011") + } + + exprList := make([]string, 0, len(s.exprList)) + for _, e := range s.exprList { + exprList = append(exprList, e.String()) + } + return nil, ctx.db.insertIndex2(s.tableName, s.indexName, exprList, s.unique, false, h) +} + +func (s *createIndexStmt) isUpdating() bool { return true } + +type createTableStmt struct { + ifNotExists bool + tableName string + cols []*col +} + +func (s *createTableStmt) explain(ctx *execCtx, w strutil.Formatter) { + w.Format("%s\n", s) +} + +func (s *createTableStmt) String() string { + a := make([]string, len(s.cols)) + for i, v := range s.cols { + var c, d string + if x := v.constraint; x != nil { + switch e := x.expr; { + case e != nil: + c = " " + e.String() + default: + c = " NOT NULL" + } + } + if x := v.dflt; x != nil { + d = " DEFAULT " + x.String() + } + a[i] = fmt.Sprintf("%s %s%s%s", v.name, typeStr(v.typ), c, d) + } + e := "" + if s.ifNotExists { + e = "IF NOT EXISTS " + } + return fmt.Sprintf("CREATE TABLE %s%s (%s);", e, s.tableName, strings.Join(a, ", ")) +} + +func (s *createTableStmt) exec(ctx *execCtx) (_ Recordset, err error) { + var cols []*col + for _, v := range s.cols { + cols = append(cols, v.clone()) + } + root := ctx.db.root + if _, ok := root.tables[s.tableName]; ok { + if s.ifNotExists { + return nil, nil + } + + return nil, fmt.Errorf("CREATE TABLE: table exists %s", s.tableName) + } + + if t, x := root.findIndexByName(s.tableName); x != nil { + return nil, fmt.Errorf("CREATE TABLE: table %s has index %s", t.name, s.tableName) + } + + m := map[string]bool{} + mustCreateColumn2 := true + for i, c := range cols { + nm := c.name + if m[nm] { + return nil, fmt.Errorf("CREATE TABLE: duplicate column %s", nm) + } + + m[nm] = true + c.index = i + if c.constraint != nil || c.dflt != nil { + if mustCreateColumn2 { + for _, stmt := range createColumn2.l { + _, err := stmt.exec(&execCtx{db: ctx.db}) + if err != nil { + return nil, err + } + } + } + + mustCreateColumn2 = false + notNull := c.constraint != nil && c.constraint.expr == nil + var co, d string + if c.constraint != nil && c.constraint.expr != nil { + co = c.constraint.expr.String() + } + if e := c.dflt; e != nil { + d = e.String() + } + if _, err := insertColumn2.l[0].exec(&execCtx{db: ctx.db, arg: []interface{}{s.tableName, c.name, notNull, co, d}}); err != nil { + return nil, err + } + } + } + t, err := root.createTable(s.tableName, cols) + if err != nil { + return nil, err + } + + return nil, t.constraintsAndDefaults(ctx) +} + +func (s *createTableStmt) isUpdating() bool { return true } diff --git a/Godeps/_workspace/src/github.com/cznic/ql/storage.go b/Godeps/_workspace/src/github.com/cznic/ql/storage.go new file mode 100644 index 000000000..fa7a4bf0c --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/storage.go @@ -0,0 +1,991 @@ +// Copyright (c) 2014 ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "fmt" + "strings" +) + +type storage interface { + Acid() bool + BeginTransaction() error + Close() error + Commit() error + Create(data ...interface{}) (h int64, err error) + CreateIndex(unique bool) (handle int64, x btreeIndex, err error) + CreateTemp(asc bool) (bt temp, err error) + Delete(h int64, blobCols ...*col) error //LATER split the nil blobCols case + ID() (id int64, err error) + Name() string + OpenIndex(unique bool, handle int64) (btreeIndex, error) // Never called on the memory backend. + Read(dst []interface{}, h int64, cols ...*col) (data []interface{}, err error) + ResetID() (err error) + Rollback() error + Update(h int64, data ...interface{}) error + UpdateRow(h int64, blobCols []*col, data ...interface{}) error + Verify() (allocs int64, err error) +} + +type btreeIterator interface { + Next() (k, v []interface{}, err error) +} + +type temp interface { + BeginTransaction() error + Create(data ...interface{}) (h int64, err error) + Drop() (err error) + Get(k []interface{}) (v []interface{}, err error) + Read(dst []interface{}, h int64, cols ...*col) (data []interface{}, err error) + SeekFirst() (e btreeIterator, err error) + Set(k, v []interface{}) (err error) +} + +type indexIterator interface { + Next() (k []interface{}, h int64, err error) + Prev() (k []interface{}, h int64, err error) +} + +type btreeIndex interface { + Clear() error // supports truncate table statement + Create(indexedValues []interface{}, h int64) error // supports insert into statement + Delete(indexedValues []interface{}, h int64) error // supports delete from statement + Drop() error // supports drop table, drop index statements + Seek(indexedValues []interface{}) (iter indexIterator, hit bool, err error) // supports where clause + SeekFirst() (iter indexIterator, err error) // supports aggregate min / ascending order by + SeekLast() (iter indexIterator, err error) // supports aggregate max / descending order by +} + +type indexedCol struct { // Column name or id() index. + name string + unique bool + x btreeIndex + xroot int64 +} + +type index2 struct { // Expression list index. + unique bool + x btreeIndex + xroot int64 + sources []string + exprList []expression +} + +func (x *index2) eval(ctx *execCtx, cols []*col, id int64, r []interface{}) ([]interface{}, error) { + f, isFile := ctx.db.store.(*file) + vlist := make([]interface{}, len(x.exprList)) + m := map[interface{}]interface{}{"$id": id} + for _, col := range cols { + ci := col.index + v := interface{}(nil) + if ci < len(r) { + v = r[ci] + } + if b, ok := v.([]byte); ok && isFile { + var err error + if v, err = expand1(chunk{f: f, b: b}, nil); err != nil { + return nil, err + } + } + m[col.name] = v + } + for i, e := range x.exprList { + v, err := e.eval(ctx, m) + if err != nil { + return nil, err + } + + if ok, typ := isBlobType(v); ok { + return nil, fmt.Errorf("value of a complex index cannot be of blob-like type: %v", typ) + } + + vlist[i] = v + } + return vlist, nil +} + +type indexKey struct { + value []interface{} + h int64 +} + +// storage fields +// 0: next int64 +// 1: scols string +// 2: hhead int64 +// 3: name string +// 4: indices string - optional +// 5: hxroots int64 - optional +type table struct { + cols []*col // logical + cols0 []*col // physical + h int64 // + head int64 // head of the single linked record list + hhead int64 // handle of the head of the single linked record list + hxroots int64 + indices []*indexedCol + indices2 map[string]*index2 + name string + next int64 // single linked table list + store storage + tnext *table + tprev *table + xroots []interface{} + constraints []*constraint + defaults []expression +} + +func (t *table) hasIndices() bool { return len(t.indices) != 0 || len(t.indices2) != 0 } +func (t *table) hasIndices2() bool { return len(t.indices2) != 0 } + +func (t *table) constraintsAndDefaults(ctx *execCtx) error { + if isSystemName[t.name] { + return nil + } + + _, ok := ctx.db.root.tables["__Column2"] + if !ok { + return nil + } + + cols := t.cols + constraints := make([]*constraint, len(cols)) + defaults := make([]expression, len(cols)) + arg := []interface{}{t.name} + rs, err := selectColumn2.l[0].exec(&execCtx{db: ctx.db, arg: arg}) + if err != nil { + return err + } + + var rows [][]interface{} + ok = false + if err := rs.(recordset).do( + &execCtx{db: ctx.db, arg: arg}, + func(id interface{}, data []interface{}) (more bool, err error) { + rows = append(rows, data) + return true, nil + }, + ); err != nil { + return err + } + + for _, row := range rows { + nm := row[0].(string) + nonNull := row[1].(bool) + cexpr := row[2].(string) + dexpr := row[3].(string) + for i, c := range cols { + if c.name == nm { + var co *constraint + if nonNull || cexpr != "" { + co = &constraint{} + constraints[i] = co + if cexpr != "" { + if co.expr, err = ctx.db.str2expr(cexpr); err != nil { + return fmt.Errorf("constraint %q: %v", cexpr, err) + } + } + + t.constraints = constraints + } + if dexpr != "" { + if defaults[i], err = ctx.db.str2expr(dexpr); err != nil { + return fmt.Errorf("constraint %q: %v", dexpr, err) + } + + t.defaults = defaults + } + } + } + } + return nil +} + +func (t *table) checkConstraintsAndDefaults(ctx *execCtx, row []interface{}, m map[interface{}]interface{}) error { + cols := t.cols + + if len(t.defaults) != 0 { + // 1. + for _, c := range cols { + m[c.name] = row[c.index] + } + + // 2. + for i, c := range cols { + val := row[c.index] + expr := t.defaults[i] + if val != nil || expr == nil { + continue + } + + dval, err := expr.eval(ctx, m) + if err != nil { + return err + } + + row[c.index] = dval + if err = typeCheck(row, []*col{c}); err != nil { + return err + } + } + } + + if len(t.constraints) != 0 { + // 3. + for _, c := range cols { + m[c.name] = row[c.index] + } + + // 4. + for i, c := range cols { + constraint := t.constraints[i] + if constraint == nil { + continue + } + + val := row[c.index] + expr := constraint.expr + if expr == nil { // Constraint: NOT NULL + if val == nil { + return fmt.Errorf("column %s: constraint violation: NOT NULL", c.name) + } + + continue + } + + // Constraint is an expression + cval, err := expr.eval(ctx, m) + if err != nil { + return err + } + + if cval == nil { + return fmt.Errorf("column %s: constraint violation: %s", c.name, expr) + } + + bval, ok := cval.(bool) + if !ok { + return fmt.Errorf("column %s: non bool constraint expression: %s", c.name, expr) + } + + if !bval { + return fmt.Errorf("column %s: constraint violation: %s", c.name, expr) + } + } + } + + return nil +} + +func (t *table) clone() *table { + r := &table{} + *r = *t + r.constraints = append([]*constraint(nil), t.constraints...) + r.defaults = append([]expression(nil), t.defaults...) + r.indices2 = nil + if n := len(t.indices2); n != 0 { + r.indices2 = make(map[string]*index2, n) + for k, v := range t.indices2 { + r.indices2[k] = v + } + } + r.cols = make([]*col, len(t.cols)) + for i, v := range t.cols { + c := &col{} + *c = *v + r.cols[i] = c + } + r.cols0 = make([]*col, len(t.cols0)) + for i, v := range t.cols0 { + c := &col{} + *c = *v + r.cols0[i] = c + } + r.indices = make([]*indexedCol, len(t.indices)) + for i, v := range t.indices { + if v != nil { + c := &indexedCol{} + *c = *v + r.indices[i] = c + } + } + r.xroots = make([]interface{}, len(t.xroots)) + copy(r.xroots, t.xroots) + r.tnext, r.tprev = nil, nil + return r +} + +func (t *table) findIndexByColName(name string) (*col, *indexedCol) { + for i, v := range t.indices { + if v == nil { + continue + } + + if i == 0 { + if name == "id()" { + return idCol, v + } + + continue + } + + if c := t.cols[i-1]; c.name == name { + return c, v + } + } + + return nil, nil +} + +func (t *table) findIndexByName(name string) interface{} { + for _, v := range t.indices { + if v != nil && v.name == name { + return v + } + } + for k, v := range t.indices2 { + if k == name { + return v + } + } + return nil +} + +func (t *table) load() (err error) { + data, err := t.store.Read(nil, t.h) + if err != nil { + return + } + + var hasIndices bool + switch n := len(data); n { + case 4: + case 6: + hasIndices = true + default: + return fmt.Errorf("corrupted DB: table data len %d", n) + } + + var ok bool + if t.next, ok = data[0].(int64); !ok { + return fmt.Errorf("corrupted DB: table data[0] of type %T", data[0]) + } + + scols, ok := data[1].(string) + if !ok { + return fmt.Errorf("corrupted DB: table data[1] of type %T", data[1]) + } + + if t.hhead, ok = data[2].(int64); !ok { + return fmt.Errorf("corrupted DB: table data[2] of type %T", data[2]) + } + + if t.name, ok = data[3].(string); !ok { + return fmt.Errorf("corrupted DB: table data[3] of type %T", data[3]) + } + + var head []interface{} + if head, err = t.store.Read(nil, t.hhead); err != nil { + return err + } + + if len(head) != 1 { + return fmt.Errorf("corrupted DB: table head data len %d", len(head)) + } + + if t.head, ok = head[0].(int64); !ok { + return fmt.Errorf("corrupted DB: table head data[0] of type %T", head[0]) + } + + a := strings.Split(scols, "|") + t.cols0 = make([]*col, len(a)) + for i, v := range a { + if len(v) < 1 { + return fmt.Errorf("corrupted DB: field info %q", v) + } + + col := &col{name: v[1:], typ: int(v[0]), index: i} + t.cols0[i] = col + if col.name != "" { + t.cols = append(t.cols, col) + } + } + + if !hasIndices { + return + } + + if t.hxroots, ok = data[5].(int64); !ok { + return fmt.Errorf("corrupted DB: table data[5] of type %T", data[5]) + } + + xroots, err := t.store.Read(nil, t.hxroots) + if err != nil { + return err + } + + if g, e := len(xroots), len(t.cols0)+1; g != e { + return fmt.Errorf("corrupted DB: got %d index roots, expected %d", g, e) + } + + indices, ok := data[4].(string) + if !ok { + return fmt.Errorf("corrupted DB: table data[4] of type %T", data[4]) + } + + a = strings.Split(indices, "|") + if g, e := len(a), len(t.cols0)+1; g != e { + return fmt.Errorf("corrupted DB: got %d index definitions, expected %d", g, e) + } + + t.indices = make([]*indexedCol, len(a)) + for i, v := range a { + if v == "" { + continue + } + + if len(v) < 2 { + return fmt.Errorf("corrupted DB: invalid index definition %q", v) + } + + nm := v[1:] + h, ok := xroots[i].(int64) + if !ok { + return fmt.Errorf("corrupted DB: table index root of type %T", xroots[i]) + } + + if h == 0 { + return fmt.Errorf("corrupted DB: missing root for index %s", nm) + } + + unique := v[0] == 'u' + x, err := t.store.OpenIndex(unique, h) + if err != nil { + return err + } + + t.indices[i] = &indexedCol{nm, unique, x, h} + } + t.xroots = xroots + + return +} + +func newTable(store storage, name string, next int64, cols []*col, tprev, tnext *table) (t *table, err error) { + hhead, err := store.Create(int64(0)) + if err != nil { + return + } + + scols := cols2meta(cols) + h, err := store.Create(next, scols, hhead, name) + if err != nil { + return + } + + t = &table{ + cols0: cols, + h: h, + hhead: hhead, + name: name, + next: next, + store: store, + tnext: tnext, + tprev: tprev, + } + return t.updateCols(), nil +} + +func (t *table) blobCols() (r []*col) { + for _, c := range t.cols0 { + switch c.typ { + case qBlob, qBigInt, qBigRat, qTime, qDuration: + r = append(r, c) + } + } + return +} + +func (t *table) truncate() (err error) { + h := t.head + var rec []interface{} + blobCols := t.blobCols() + for h != 0 { + rec, err := t.store.Read(rec, h) + if err != nil { + return err + } + nh := rec[0].(int64) + + if err = t.store.Delete(h, blobCols...); err != nil { //LATER remove double read for len(blobCols) != 0 + return err + } + + h = nh + } + if err = t.store.Update(t.hhead, 0); err != nil { + return + } + + for _, v := range t.indices { + if v == nil { + continue + } + + if err := v.x.Clear(); err != nil { + return err + } + } + for _, ix := range t.indices2 { + if err := ix.x.Clear(); err != nil { + return err + } + } + t.head = 0 + return t.updated() +} + +func (t *table) addIndex0(unique bool, indexName string, colIndex int) (int64, btreeIndex, error) { + switch len(t.indices) { + case 0: + indices := make([]*indexedCol, len(t.cols0)+1) + h, x, err := t.store.CreateIndex(unique) + if err != nil { + return -1, nil, err + } + + indices[colIndex+1] = &indexedCol{indexName, unique, x, h} + xroots := make([]interface{}, len(indices)) + xroots[colIndex+1] = h + hx, err := t.store.Create(xroots...) + if err != nil { + return -1, nil, err + } + + t.hxroots, t.xroots, t.indices = hx, xroots, indices + return h, x, t.updated() + default: + ex := t.indices[colIndex+1] + if ex != nil && ex.name != "" { + colName := "id()" + if colIndex >= 0 { + colName = t.cols0[colIndex].name + } + return -1, nil, fmt.Errorf("column %s already has an index: %s", colName, ex.name) + } + + h, x, err := t.store.CreateIndex(unique) + if err != nil { + return -1, nil, err + } + + t.xroots[colIndex+1] = h + if err := t.store.Update(t.hxroots, t.xroots...); err != nil { + return -1, nil, err + } + + t.indices[colIndex+1] = &indexedCol{indexName, unique, x, h} + return h, x, t.updated() + } +} + +func (t *table) addIndex(unique bool, indexName string, colIndex int) (int64, error) { + hx, x, err := t.addIndex0(unique, indexName, colIndex) + if err != nil { + return -1, err + } + + // Must fill the new index. + ncols := len(t.cols0) + h, store := t.head, t.store + for h != 0 { + rec, err := store.Read(nil, h, t.cols...) + if err != nil { + return -1, err + } + + if n := ncols + 2 - len(rec); n > 0 { + rec = append(rec, make([]interface{}, n)...) + } + + if err = x.Create([]interface{}{rec[colIndex+2]}, h); err != nil { + return -1, err + } + + h = rec[0].(int64) + } + return hx, nil +} + +func (t *table) addIndex2(execCtx *execCtx, unique bool, indexName string, exprList []expression) (int64, error) { + if _, ok := t.indices2[indexName]; ok { + panic("internal error 009") + } + + hx, x, err := t.store.CreateIndex(unique) + if err != nil { + return -1, err + } + var a []string + for _, v := range exprList { + a = append(a, v.String()) + } + x2 := &index2{unique, x, hx, a, exprList} + if t.indices2 == nil { + t.indices2 = map[string]*index2{} + } + t.indices2[indexName] = x2 + + // Must fill the new index. + m := map[interface{}]interface{}{} + h, store := t.head, t.store + for h != 0 { + rec, err := store.Read(nil, h, t.cols...) + if err != nil { + return -1, err + } + + for _, col := range t.cols { + ci := col.index + v := interface{}(nil) + if ci < len(rec) { + v = rec[ci+2] + } + m[col.name] = v + } + + id := rec[1].(int64) + vlist, err := x2.eval(execCtx, t.cols, id, rec[2:]) + if err != nil { + return -1, err + } + + if err := x2.x.Create(vlist, h); err != nil { + return -1, err + } + + h = rec[0].(int64) + } + return hx, nil +} + +func (t *table) dropIndex(xIndex int) error { + t.xroots[xIndex] = 0 + if err := t.indices[xIndex].x.Drop(); err != nil { + return err + } + + t.indices[xIndex] = nil + return t.updated() +} + +func (t *table) updated() (err error) { + switch { + case len(t.indices) != 0: + a := []string{} + for _, v := range t.indices { + if v == nil { + a = append(a, "") + continue + } + + s := "n" + if v.unique { + s = "u" + } + a = append(a, s+v.name) + } + return t.store.Update(t.h, t.next, cols2meta(t.updateCols().cols0), t.hhead, t.name, strings.Join(a, "|"), t.hxroots) + default: + return t.store.Update(t.h, t.next, cols2meta(t.updateCols().cols0), t.hhead, t.name) + } +} + +// storage fields +// 0: next record handle int64 +// 1: record id int64 +// 2...: data row +func (t *table) addRecord(execCtx *execCtx, r []interface{}) (id int64, err error) { + if id, err = t.store.ID(); err != nil { + return + } + + r = append([]interface{}{t.head, id}, r...) + h, err := t.store.Create(r...) + if err != nil { + return + } + + for i, v := range t.indices { + if v == nil { + continue + } + + if err = v.x.Create([]interface{}{r[i+1]}, h); err != nil { + return + } + } + + for _, ix := range t.indices2 { + vlist, err := ix.eval(execCtx, t.cols, id, r[2:]) + if err != nil { + return -1, err + } + + if err := ix.x.Create(vlist, h); err != nil { + return -1, err + } + } + + if err = t.store.Update(t.hhead, h); err != nil { + return + } + + t.head = h + return +} + +func (t *table) flds() (r []*fld) { + r = make([]*fld, len(t.cols)) + for i, v := range t.cols { + r[i] = &fld{expr: &ident{v.name}, name: v.name} + } + return +} + +func (t *table) fieldNames() []string { + r := make([]string, len(t.cols)) + for i, v := range t.cols { + r[i] = v.name + } + return r +} + +func (t *table) updateCols() *table { + t.cols = t.cols[:0] + for i, c := range t.cols0 { + if c.name != "" { + c.index = i + t.cols = append(t.cols, c) + } + } + return t +} + +func (t *table) row0(ctx *execCtx, h int64) ([]interface{}, error) { + rec, err := ctx.db.store.Read(nil, h, t.cols...) + if err != nil { + return nil, err + } + + if d := len(t.cols) - (len(rec) - 2); d > 0 { + rec = append(rec, make([]interface{}, d)...) + } + + return rec, nil +} + +func (t *table) row(ctx *execCtx, h int64) (int64, []interface{}, error) { + rec, err := t.row0(ctx, h) + if err != nil { + return -1, nil, err + } + + return rec[1].(int64), rec[2:], nil +} + +// storage fields +// 0: handle of first table in DB int64 +type root struct { + head int64 // Single linked table list + lastInsertID int64 + parent *root + rowsAffected int64 //LATER implement + store storage + tables map[string]*table + thead *table +} + +func newRoot(store storage) (r *root, err error) { + data, err := store.Read(nil, 1) + if err != nil { + return + } + + switch len(data) { + case 0: // new empty DB, create empty table list + if err = store.BeginTransaction(); err != nil { + return + } + + if err = store.Update(1, int64(0)); err != nil { + store.Rollback() + return + } + + if err = store.Commit(); err != nil { + return + } + + return &root{ + store: store, + tables: map[string]*table{}, + }, nil + case 1: // existing DB, load tables + if len(data) != 1 { + return nil, fmt.Errorf("corrupted DB: root is an %d-scalar", len(data)) + } + + p, ok := data[0].(int64) + if !ok { + return nil, fmt.Errorf("corrupted DB: root head has type %T", data[0]) + } + + r := &root{ + head: p, + store: store, + tables: map[string]*table{}, + } + + var tprev *table + for p != 0 { + t := &table{ + h: p, + store: store, + tprev: tprev, + } + + if r.thead == nil { + r.thead = t + } + if tprev != nil { + tprev.tnext = t + } + tprev = t + + if err = t.load(); err != nil { + return nil, err + } + + if r.tables[t.name] != nil { // duplicate + return nil, fmt.Errorf("corrupted DB: duplicate table metadata for table %s", t.name) + } + + r.tables[t.name] = t + p = t.next + } + return r, nil + default: + return nil, errIncompatibleDBFormat + } +} + +func (r *root) findIndexByName(name string) (*table, interface{}) { + for _, t := range r.tables { + if i := t.findIndexByName(name); i != nil { + return t, i + } + } + + return nil, nil +} + +func (r *root) updated() (err error) { + return r.store.Update(1, r.head) +} + +func (r *root) createTable(name string, cols []*col) (t *table, err error) { + if _, ok := r.tables[name]; ok { + panic("internal error 065") + } + + if t, err = newTable(r.store, name, r.head, cols, nil, r.thead); err != nil { + return nil, err + } + + if err = r.store.Update(1, t.h); err != nil { + return nil, err + } + + if p := r.thead; p != nil { + p.tprev = t + } + r.tables[name], r.head, r.thead = t, t.h, t + return +} + +func (r *root) dropTable(t *table) (err error) { + defer func() { + if err != nil { + return + } + + delete(r.tables, t.name) + }() + + if err = t.truncate(); err != nil { + return + } + + if err = t.store.Delete(t.hhead); err != nil { + return + } + + if err = t.store.Delete(t.h); err != nil { + return + } + + for _, v := range t.indices { + if v != nil && v.x != nil { + if err = v.x.Drop(); err != nil { + return + } + } + } + for _, v := range t.indices2 { + if err = v.x.Drop(); err != nil { + return + } + } + + if h := t.hxroots; h != 0 { + if err = t.store.Delete(h); err != nil { + return + } + } + + switch { + case t.tprev == nil && t.tnext == nil: + r.head = 0 + r.thead = nil + err = r.updated() + return errSet(&err, r.store.ResetID()) + case t.tprev == nil && t.tnext != nil: + next := t.tnext + next.tprev = nil + r.head = next.h + r.thead = next + if err = r.updated(); err != nil { + return + } + + return next.updated() + case t.tprev != nil && t.tnext == nil: // last in list + prev := t.tprev + prev.next = 0 + prev.tnext = nil + return prev.updated() + default: //case t.tprev != nil && t.tnext != nil: + prev, next := t.tprev, t.tnext + prev.next = next.h + prev.tnext = next + next.tprev = prev + if err = prev.updated(); err != nil { + return + } + + return next.updated() + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/storage_test.go b/Godeps/_workspace/src/github.com/cznic/ql/storage_test.go new file mode 100644 index 000000000..707dcf610 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/storage_test.go @@ -0,0 +1,495 @@ +// Copyright (c) 2014 ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ql + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "regexp" + "runtime/debug" + "strings" + "testing" +) + +var ( + oN = flag.Int("N", 0, "") + oM = flag.Int("M", 0, "") + oFastFail = flag.Bool("fastFail", false, "") + oSlow = flag.Bool("slow", false, "Do not wrap storage tests in a single outer transaction, write everything to disk file. Very slow.") +) + +var testdata []string + +func init() { + tests, err := ioutil.ReadFile("testdata.ql") + if err != nil { + log.Panic(err) + } + + a := bytes.Split(tests, []byte("\n-- ")) + pre := []byte("-- ") + pres := []byte("S ") + for _, v := range a[1:] { + switch { + case bytes.HasPrefix(v, pres): + v = append(pre, v...) + v = append([]byte(sample), v...) + default: + v = append(pre, v...) + } + testdata = append(testdata, string(v)) + } +} + +func typeof(v interface{}) (r int) { //NTYPE + switch v.(type) { + case bool: + return qBool + case complex64: + return qComplex64 + case complex128: + return qComplex128 + case float32: + return qFloat32 + case float64: + return qFloat64 + case int8: + return qInt8 + case int16: + return qInt16 + case int32: + return qInt32 + case int64: + return qInt64 + case string: + return qString + case uint8: + return qUint8 + case uint16: + return qUint16 + case uint32: + return qUint32 + case uint64: + return qUint64 + } + return +} + +func stypeof(nm string, val interface{}) string { + if t := typeof(val); t != 0 { + return fmt.Sprintf("%c%s", t, nm) + } + + switch val.(type) { + case idealComplex: + return fmt.Sprintf("c%s", nm) + case idealFloat: + return fmt.Sprintf("f%s", nm) + case idealInt: + return fmt.Sprintf("l%s", nm) + case idealRune: + return fmt.Sprintf("k%s", nm) + case idealUint: + return fmt.Sprintf("x%s", nm) + default: + return fmt.Sprintf("?%s", nm) + } +} + +func dumpCols(cols []*col) string { + a := []string{} + for _, col := range cols { + a = append(a, fmt.Sprintf("%d:%s %s", col.index, col.name, typeStr(col.typ))) + } + return strings.Join(a, ",") +} + +func dumpFlds(flds []*fld) string { + a := []string{} + for _, fld := range flds { + a = append(a, fmt.Sprintf("%s AS %s", fld.expr, fld.name)) + } + return strings.Join(a, ",") +} + +func recSetDump(rs Recordset) (s string, err error) { + recset := rs.(recordset) + p := recset.plan + a0 := append([]string(nil), p.fieldNames()...) + for i, v := range a0 { + a0[i] = fmt.Sprintf("%q", v) + } + a := []string{strings.Join(a0, ", ")} + if err := p.do(recset.ctx, func(id interface{}, data []interface{}) (bool, error) { + if err = expand(data); err != nil { + return false, err + } + + a = append(a, fmt.Sprintf("%v", data)) + return true, nil + }); err != nil { + return "", err + } + return strings.Join(a, "\n"), nil +} + +// http://en.wikipedia.org/wiki/Join_(SQL)#Sample_tables +const sample = ` + BEGIN TRANSACTION; + CREATE TABLE department ( + DepartmentID int, + DepartmentName string, + ); + + INSERT INTO department VALUES + (31, "Sales"), + (33, "Engineering"), + (34, "Clerical"), + (35, "Marketing"), + ; + + CREATE TABLE employee ( + LastName string, + DepartmentID int, + ); + + INSERT INTO employee VALUES + ("Rafferty", 31), + ("Jones", 33), + ("Heisenberg", 33), + ("Robinson", 34), + ("Smith", 34), + ("Williams", NULL), + ; + COMMIT; +` + +func explained(db *DB, s stmt, tctx *TCtx) (string, error) { + src := "explain " + s.String() + rs, _, err := db.Run(tctx, src, int64(30)) + if err != nil { + return "", err + } + + rows, err := rs[0].Rows(-1, 0) + if err != nil { + return "", err + } + + if !strings.HasPrefix(rows[0][0].(string), "┌") { + return "", nil + } + + var a []string + for _, v := range rows { + a = append(a, v[0].(string)) + } + return strings.Join(a, "\n"), nil +} + +// Test provides a testing facility for alternative storage implementations. +// The s.setup should return a freshly created and empty storage. Removing the +// store from the system is the responsibility of the caller. The test only +// guarantees not to panic on recoverable errors and return an error instead. +// Test errors are not returned but reported to t. +func test(t *testing.T, s testDB) (panicked error) { + defer func() { + if e := recover(); e != nil { + switch x := e.(type) { + case error: + panicked = x + default: + panicked = fmt.Errorf("%v", e) + } + } + if panicked != nil { + t.Errorf("PANIC: %v\n%s", panicked, debug.Stack()) + } + }() + + db, err := s.setup() + if err != nil { + t.Error(err) + return + } + + tctx := NewRWCtx() + if !*oSlow { + if _, _, err := db.Execute(tctx, txBegin); err != nil { + t.Error(err) + return nil + } + } + + if err = s.mark(); err != nil { + t.Error(err) + return + } + + defer func() { + x := tctx + if *oSlow { + x = nil + } + if err = s.teardown(x); err != nil { + t.Error(err) + } + }() + + chk := func(test int, err error, expErr string, re *regexp.Regexp) (ok bool) { + s := err.Error() + if re == nil { + t.Error("FAIL: ", test, s) + return false + } + + if !re.MatchString(s) { + t.Error("FAIL: ", test, "error doesn't match:", s, "expected", expErr) + return false + } + + return true + } + + var logf *os.File + hasLogf := false + noErrors := true + if _, ok := s.(*memTestDB); ok { + if logf, err = ioutil.TempFile("", "ql-test-log-"); err != nil { + t.Error(err) + return nil + } + + hasLogf = true + } else { + if logf, err = os.Create(os.DevNull); err != nil { + t.Error(err) + return nil + } + } + + defer func() { + if hasLogf && noErrors { + func() { + if _, err := logf.Seek(0, 0); err != nil { + t.Error(err) + return + } + + dst, err := os.Create("testdata.log") + if err != nil { + t.Error(err) + return + } + + if _, err := io.Copy(dst, logf); err != nil { + t.Error(err) + return + } + + if err := dst.Close(); err != nil { + t.Error(err) + } + }() + } + + nm := logf.Name() + if err := logf.Close(); err != nil { + t.Error(err) + } + + if hasLogf { + if err := os.Remove(nm); err != nil { + t.Error(err) + } + } + }() + + log := bufio.NewWriter(logf) + + defer func() { + if err := log.Flush(); err != nil { + t.Error(err) + } + }() + + max := len(testdata) + if n := *oM; n != 0 && n < max { + max = n + } + for itest, test := range testdata[*oN:max] { + //dbg("------------------------------------------------------------- ( itest %d ) ----", itest) + var re *regexp.Regexp + a := strings.Split(test+"|", "|") + q, rset := a[0], strings.TrimSpace(a[1]) + var expErr string + if len(a) < 3 { + t.Error(itest, "internal error 066") + return + } + + if expErr = a[2]; expErr != "" { + re = regexp.MustCompile("(?i:" + strings.TrimSpace(expErr) + ")") + } + + q = strings.Replace(q, "∨", "|", -1) + q = strings.Replace(q, "⩖", "||", -1) + list, err := Compile(q) + if err != nil { + if !chk(itest, err, expErr, re) && *oFastFail { + return + } + + continue + } + + for _, s := range list.l { + if err := testMentionedColumns(s); err != nil { + t.Error(itest, err) + return + } + } + + s1 := list.String() + list1, err := Compile(s1) + if err != nil { + t.Errorf("recreated source does not compile: %v\n---- orig\n%s\n---- recreated\n%s", err, q, s1) + if *oFastFail { + return + } + + continue + } + + s2 := list1.String() + if g, e := s2, s1; g != e { + t.Errorf("recreated source is not idempotent\n---- orig\n%s\n---- recreated1\n%s\n---- recreated2\n%s", q, s1, s2) + if *oFastFail { + return + } + + continue + } + + if !func() (ok bool) { + tnl0 := db.tnl + defer func() { + s3 := list.String() + if g, e := s1, s3; g != e { + t.Errorf("#%d: execution mutates compiled statement list\n---- orig\n%s----new\n%s", itest, g, e) + } + + if !ok { + noErrors = false + } + + if noErrors { + hdr := false + for _, v := range list.l { + s, err := explained(db, v, tctx) + if err != nil { + t.Error(err) + return + } + + if !strings.HasPrefix(s, "┌") { + continue + } + + if !hdr { + fmt.Fprintf(log, "---- %v\n", itest) + hdr = true + } + fmt.Fprintf(log, "%s\n", v) + fmt.Fprintf(log, "%s\n\n", s) + } + } + + tnl := db.tnl + if tnl != tnl0 { + panic(fmt.Errorf("internal error 057: tnl0 %v, tnl %v", tnl0, tnl)) + } + + nfo, err := db.Info() + if err != nil { + dbg("", err) + panic(err) + } + + for _, idx := range nfo.Indices { + //dbg("#%d: cleanup index %s", itest, idx.Name) + if _, _, err = db.run(tctx, fmt.Sprintf(` + BEGIN TRANSACTION; + DROP INDEX %s; + COMMIT; + `, + idx.Name)); err != nil { + t.Errorf("#%d: cleanup DROP INDEX %s: %v", itest, idx.Name, err) + ok = false + } + } + for _, tab := range nfo.Tables { + //dbg("#%d: cleanup table %s", itest, tab.Name) + if _, _, err = db.run(tctx, fmt.Sprintf(` + BEGIN TRANSACTION; + DROP table %s; + COMMIT; + `, + tab.Name)); err != nil { + t.Errorf("#%d: cleanup DROP TABLE %s: %v", itest, tab.Name, err) + ok = false + } + } + db.hasIndex2 = 0 + }() + + if err = s.mark(); err != nil { + t.Error(err) + return + } + + rs, _, err := db.Execute(tctx, list, int64(30)) + if err != nil { + return chk(itest, err, expErr, re) + } + + if rs == nil { + t.Errorf("FAIL: %d: expected non nil Recordset or error %q", itest, expErr) + return + } + + g, err := recSetDump(rs[len(rs)-1]) + if err != nil { + return chk(itest, err, expErr, re) + } + + if expErr != "" { + t.Errorf("FAIL: %d: expected error %q", itest, expErr) + return + } + + a = strings.Split(rset, "\n") + for i, v := range a { + a[i] = strings.TrimSpace(v) + } + e := strings.Join(a, "\n") + if g != e { + t.Errorf("FAIL: test # %d\n%s\n---- g\n%s\n---- e\n%s\n----", itest, q, g, e) + return + } + + return true + }() && *oFastFail { + return + } + } + return +} diff --git a/Godeps/_workspace/src/github.com/cznic/ql/testdata.log b/Godeps/_workspace/src/github.com/cznic/ql/testdata.log new file mode 100644 index 000000000..0e13dcfbb --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/testdata.log @@ -0,0 +1,8382 @@ +---- 0 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2" "c3"] + +---- 3 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2" "c3" "c4"] + +---- 8 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c3"] + +---- 15 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] + +---- 19 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2" "c3" "c4"] + +---- 20 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2" "c4"] + +---- 22 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 24 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] + +---- 28 +SELECT 3 * c1 AS v FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Evaluate 3 * c1 as "v", +└Output field names ["v"] + +---- 29 +SELECT c2 FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Evaluate c2 as "c2", +└Output field names ["c2"] + +---- 30 +SELECT c1 AS X, c2 FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Evaluate c1 as "X", c2 as "c2", +└Output field names ["X" "c2"] + +---- 31 +SELECT c2, c1 AS Y FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Evaluate c2 as "c2", c1 as "Y", +└Output field names ["c2" "Y"] + +---- 33 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1" "c2"] + +---- 35 +SELECT * FROM t ORDER BY c1; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Order by c1, +└Output field names ["c1" "c2"] + +---- 36 +SELECT * FROM t ORDER BY c1; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Order by c1, +└Output field names ["c1" "c2"] + +---- 37 +SELECT * FROM t ORDER BY c1 DESC; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Order descending by c1, +└Output field names ["c1" "c2"] + +---- 38 +SELECT * FROM t WHERE c1 % 2 == 0 ORDER BY c2 DESC; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Filter on c1 % 2 == 0 +└Output field names ["c1" "c2"] +┌Order descending by c2, +└Output field names ["c1" "c2"] + +---- 39 +SELECT * FROM t ORDER BY c1, c2; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Order by c1, c2, +└Output field names ["c1" "c2"] + +---- 40 +SELECT * FROM t ORDER BY c2, c1; +┌Iterate all rows of table "t" +└Output field names ["c1" "c2"] +┌Order by c2, c1, +└Output field names ["c1" "c2"] + +---- 44 +SELECT employee.LastName FROM employee, department; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate employee.LastName as "employee.LastName", +└Output field names ["employee.LastName"] + +---- 45 +SELECT * FROM employee, department ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by employee.LastName, +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 46 +SELECT * FROM employee, department WHERE employee.DepartmentID == department.DepartmentID; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 47 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID FROM employee, department WHERE employee.DepartmentID == department.DepartmentID ORDER BY department.DepartmentName, employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate department.DepartmentName as "department.DepartmentName", department.DepartmentID as "department.DepartmentID", employee.LastName as "employee.LastName", employee.DepartmentID as "employee.DepartmentID", +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] +┌Order by department.DepartmentName, employee.LastName, +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] + +---- 48 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID FROM employee, department WHERE department.DepartmentName IN ("Sales","Engineering","HR","Clerical") ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on department.DepartmentName IN ("Sales","Engineering","HR","Clerical") +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate department.DepartmentName as "department.DepartmentName", department.DepartmentID as "department.DepartmentID", employee.LastName as "employee.LastName", employee.DepartmentID as "employee.DepartmentID", +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] +┌Order by employee.LastName, +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] + +---- 49 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID FROM employee, department WHERE (department.DepartmentID + 1000) IN (1031,1035,1036) ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on (department.DepartmentID + 1000) IN (1031,1035,1036) +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate department.DepartmentName as "department.DepartmentName", department.DepartmentID as "department.DepartmentID", employee.LastName as "employee.LastName", employee.DepartmentID as "employee.DepartmentID", +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] +┌Order by employee.LastName, +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] + +---- 50 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID FROM employee, department WHERE department.DepartmentName NOT IN ("Engineering","HR","Clerical"); +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on department.DepartmentName NOT IN ("Engineering","HR","Clerical") +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate department.DepartmentName as "department.DepartmentName", department.DepartmentID as "department.DepartmentID", employee.LastName as "employee.LastName", employee.DepartmentID as "employee.DepartmentID", +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] + +---- 51 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID FROM employee, department WHERE department.DepartmentID >= 34 && department.DepartmentID <= 36 ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on department.DepartmentID >= 34 && department.DepartmentID <= 36 +│Possibly useful indices +│CREATE INDEX xdepartment_DepartmentID ON department(DepartmentID); +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate department.DepartmentName as "department.DepartmentName", department.DepartmentID as "department.DepartmentID", employee.LastName as "employee.LastName", employee.DepartmentID as "employee.DepartmentID", +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] +┌Order by employee.LastName, +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] + +---- 52 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID FROM employee, department WHERE department.DepartmentID >= 34 && department.DepartmentID <= 36 ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on department.DepartmentID >= 34 && department.DepartmentID <= 36 +│Possibly useful indices +│CREATE INDEX xdepartment_DepartmentID ON department(DepartmentID); +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate department.DepartmentName as "department.DepartmentName", department.DepartmentID as "department.DepartmentID", employee.LastName as "employee.LastName", employee.DepartmentID as "employee.DepartmentID", +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] +┌Order by employee.LastName, +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] + +---- 53 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID FROM employee, department WHERE department.DepartmentID < 33 || department.DepartmentID > 34 ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on department.DepartmentID < 33 || department.DepartmentID > 34 +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate department.DepartmentName as "department.DepartmentName", department.DepartmentID as "department.DepartmentID", employee.LastName as "employee.LastName", employee.DepartmentID as "employee.DepartmentID", +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] +┌Order by employee.LastName, +└Output field names ["department.DepartmentName" "department.DepartmentID" "employee.LastName" "employee.DepartmentID"] + +---- 56 +SELECT LastName AS a, LastName AS b FROM employee ORDER BY a, b; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "a", LastName as "b", +└Output field names ["a" "b"] +┌Order by a, b, +└Output field names ["a" "b"] + +---- 57 +SELECT employee.LastName AS name, employee.DepartmentID AS id, department.DepartmentName AS department, department.DepartmentID AS id2 FROM employee, department WHERE employee.DepartmentID == department.DepartmentID ORDER BY name, id, department, id2; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate employee.LastName as "name", employee.DepartmentID as "id", department.DepartmentName as "department", department.DepartmentID as "id2", +└Output field names ["name" "id" "department" "id2"] +┌Order by name, id, department, id2, +└Output field names ["name" "id" "department" "id2"] + +---- 59 +SELECT * FROM employee ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 60 +SELECT * FROM employee AS e ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 65 +SELECT * FROM (SELECT * FROM employee;) ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 66 +SELECT * FROM (SELECT LastName AS Name FROM employee;) ORDER BY Name; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "Name", +└Output field names ["Name"] +┌Order by Name, +└Output field names ["Name"] + +---- 68 +SELECT name AS Name FROM (SELECT LastName AS name FROM employee AS e;) ORDER BY Name; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "name", +└Output field names ["name"] +┌Evaluate name as "Name", +└Output field names ["Name"] +┌Order by Name, +└Output field names ["Name"] + +---- 69 +SELECT name AS Name FROM (SELECT LastName AS name FROM employee;) ORDER BY Name; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "name", +└Output field names ["name"] +┌Evaluate name as "Name", +└Output field names ["Name"] +┌Order by Name, +└Output field names ["Name"] + +---- 70 +SELECT employee.LastName, department.DepartmentName, department.DepartmentID FROM (SELECT * FROM employee, department WHERE employee.DepartmentID == department.DepartmentID;) ORDER BY department.DepartmentName, employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Evaluate employee.LastName as "employee.LastName", department.DepartmentName as "department.DepartmentName", department.DepartmentID as "department.DepartmentID", +└Output field names ["employee.LastName" "department.DepartmentName" "department.DepartmentID"] +┌Order by department.DepartmentName, employee.LastName, +└Output field names ["employee.LastName" "department.DepartmentName" "department.DepartmentID"] + +---- 71 +SELECT e.LastName, d.DepartmentName, d.DepartmentID FROM (SELECT * FROM employee AS e, department AS d WHERE e.DepartmentID == d.DepartmentID;) ORDER BY d.DepartmentName, e.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Evaluate e.LastName as "e.LastName", d.DepartmentName as "d.DepartmentName", d.DepartmentID as "d.DepartmentID", +└Output field names ["e.LastName" "d.DepartmentName" "d.DepartmentID"] +┌Order by d.DepartmentName, e.LastName, +└Output field names ["e.LastName" "d.DepartmentName" "d.DepartmentID"] + +---- 72 +SELECT e.LastName AS name, d.DepartmentName AS department, d.DepartmentID AS id FROM (SELECT * FROM employee AS e, department AS d WHERE e.DepartmentID == d.DepartmentID;) ORDER BY department, name; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Evaluate e.LastName as "name", d.DepartmentName as "department", d.DepartmentID as "id", +└Output field names ["name" "department" "id"] +┌Order by department, name, +└Output field names ["name" "department" "id"] + +---- 73 +SELECT name, department, id FROM (SELECT e.LastName AS name, e.DepartmentID AS id, d.DepartmentName AS department, d.DepartmentID AS fid FROM employee AS e, department AS d WHERE e.DepartmentID == d.DepartmentID;) ORDER BY department, name; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Evaluate e.LastName as "name", e.DepartmentID as "id", d.DepartmentName as "department", d.DepartmentID as "fid", +└Output field names ["name" "id" "department" "fid"] +┌Evaluate name as "name", department as "department", id as "id", +└Output field names ["name" "department" "id"] +┌Order by department, name, +└Output field names ["name" "department" "id"] + +---- 74 +SELECT * FROM (SELECT * FROM employee;), (SELECT * FROM department;); +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["" "" "" ""] + +---- 75 +SELECT * FROM (SELECT * FROM employee;) AS e, (SELECT * FROM department;) ORDER BY e.LastName, e.DepartmentID; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "" ""] +┌Order by e.LastName, e.DepartmentID, +└Output field names ["e.LastName" "e.DepartmentID" "" ""] + +---- 76 +SELECT * FROM (SELECT * FROM employee;), (SELECT * FROM department;) AS d ORDER BY d.DepartmentID DESC; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["" "" "d.DepartmentID" "d.DepartmentName"] +┌Order descending by d.DepartmentID, +└Output field names ["" "" "d.DepartmentID" "d.DepartmentName"] + +---- 77 +SELECT * FROM employee, (SELECT * FROM department;) ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "" ""] +┌Order by employee.LastName, +└Output field names ["employee.LastName" "employee.DepartmentID" "" ""] + +---- 78 +SELECT * FROM (SELECT * FROM employee;) AS e, (SELECT * FROM department;) AS d WHERE e.DepartmentID == d.DepartmentID ORDER BY d.DepartmentName, e.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Order by d.DepartmentName, e.LastName, +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] + +---- 79 +SELECT * FROM employee, (SELECT * FROM department;) AS d WHERE employee.DepartmentID == d.DepartmentID ORDER BY d.DepartmentName, employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on employee.DepartmentID == d.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Order by d.DepartmentName, employee.LastName, +└Output field names ["employee.LastName" "employee.DepartmentID" "d.DepartmentID" "d.DepartmentName"] + +---- 80 +SELECT * FROM employee AS e, (SELECT * FROM department;) AS d WHERE e.DepartmentID == d.DepartmentID ORDER BY d.DepartmentName, e.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Order by d.DepartmentName, e.LastName, +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] + +---- 81 +SELECT * FROM employee AS e, (SELECT * FROM department;) AS d WHERE e.DepartmentID == d.DepartmentID ORDER BY e.DepartmentID, e.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Order by e.DepartmentID, e.LastName, +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] + +---- 82 +SELECT * FROM employee AS e, (SELECT * FROM department;) AS d WHERE e.DepartmentID == d.DepartmentID ORDER BY e.DepartmentID, e.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Order by e.DepartmentID, e.LastName, +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] + +---- 84 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 87 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 88 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 92 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 94 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 95 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 96 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 98 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 102 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 104 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 106 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 107 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 109 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 110 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 111 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 112 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 113 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 115 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 116 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 118 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 119 +SELECT * FROM t WHERE c1 > 3; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 > 3 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 120 +SELECT * FROM t WHERE c1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 +└Output field names ["c1"] + +---- 122 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 123 +SELECT * FROM t WHERE c1 == 8; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 8 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 124 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 125 +SELECT * FROM t WHERE c1 == 8; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 8 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 126 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 127 +SELECT * FROM t WHERE c1 == 8; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 8 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 128 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 129 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 130 +SELECT * FROM t WHERE c1 == 8; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 8 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 131 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 132 +SELECT * FROM t WHERE c1 == 8; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 8 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 133 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 134 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 135 +SELECT * FROM t WHERE c1 == 8; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 8 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 136 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 137 +SELECT * FROM t WHERE c1 == 1; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 1 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 138 +SELECT * FROM t WHERE c1 == 8; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 8 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 139 +SELECT * FROM t WHERE c1 == 8; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 8 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 140 +SELECT * FROM t WHERE c1 == 2; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 2 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 141 +SELECT * FROM t WHERE c1 == 2; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 2 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 142 +SELECT * FROM t WHERE c1 == 2; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 2 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 143 +SELECT * FROM t WHERE c1 == "foo"; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == "foo" +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 144 +SELECT * FROM t WHERE c1 == 2+5i; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 2+5i +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 145 +SELECT * FROM t WHERE c1 == "2"; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == "2" +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 146 +SELECT * FROM t WHERE c1 == 2+5i; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 2+5i +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 147 +SELECT * FROM t WHERE c1 == 2; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == 2 +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 148 +SELECT * FROM t WHERE c1 == "foo"; +┌Iterate all rows of table "t" +└Output field names ["c1"] +┌Filter on c1 == "foo" +│Possibly useful indices +│CREATE INDEX xt_c1 ON t(c1); +└Output field names ["c1"] + +---- 153 +SELECT 314, 42 AS AUQLUE, DepartmentID, DepartmentID + 1000, LastName AS Name FROM employee ORDER BY Name; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate 314 as "", 42 as "AUQLUE", DepartmentID as "DepartmentID", DepartmentID + 1000 as "", LastName as "Name", +└Output field names ["" "AUQLUE" "DepartmentID" "" "Name"] +┌Order by Name, +└Output field names ["" "AUQLUE" "DepartmentID" "" "Name"] + +---- 154 +SELECT * FROM employee AS e, (SELECT * FROM department;) ORDER BY e.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "" ""] +┌Order by e.LastName, +└Output field names ["e.LastName" "e.DepartmentID" "" ""] + +---- 155 +SELECT * FROM employee AS e, (SELECT * FROM department;) AS d ORDER BY e.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Order by e.LastName, +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] + +---- 157 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c1"] + +---- 158 +SELECT * FROM p; +┌Iterate all rows of table "p" +└Output field names ["p"] + +---- 159 +SELECT p.p AS p, q.p AS q, p.p || q.p AS p_or_q, p.p && q.p AS p_and_q FROM p, p AS q; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "p" +│ └Output field names ["p"] +│ ┌Iterate all rows of table "p" +│ └Output field names ["p"] +└Output field names ["p.p" "q.p"] +┌Evaluate p.p as "p", q.p as "q", p.p || q.p as "p_or_q", p.p && q.p as "p_and_q", +└Output field names ["p" "q" "p_or_q" "p_and_q"] + +---- 160 +SELECT p, !p AS not_p FROM p; +┌Iterate all rows of table "p" +└Output field names ["p"] +┌Evaluate p as "p", !p as "not_p", +└Output field names ["p" "not_p"] + +---- 161 +SELECT * FROM department WHERE DepartmentID >= 33 ORDER BY DepartmentID; +┌Iterate all rows of table "department" +└Output field names ["DepartmentID" "DepartmentName"] +┌Filter on DepartmentID >= 33 +│Possibly useful indices +│CREATE INDEX xdepartment_DepartmentID ON department(DepartmentID); +└Output field names ["DepartmentID" "DepartmentName"] +┌Order by DepartmentID, +└Output field names ["DepartmentID" "DepartmentName"] + +---- 162 +SELECT * FROM department WHERE DepartmentID <= 34 ORDER BY DepartmentID; +┌Iterate all rows of table "department" +└Output field names ["DepartmentID" "DepartmentName"] +┌Filter on DepartmentID <= 34 +│Possibly useful indices +│CREATE INDEX xdepartment_DepartmentID ON department(DepartmentID); +└Output field names ["DepartmentID" "DepartmentName"] +┌Order by DepartmentID, +└Output field names ["DepartmentID" "DepartmentName"] + +---- 163 +SELECT * FROM department WHERE DepartmentID < 34 ORDER BY DepartmentID; +┌Iterate all rows of table "department" +└Output field names ["DepartmentID" "DepartmentName"] +┌Filter on DepartmentID < 34 +│Possibly useful indices +│CREATE INDEX xdepartment_DepartmentID ON department(DepartmentID); +└Output field names ["DepartmentID" "DepartmentName"] +┌Order by DepartmentID, +└Output field names ["DepartmentID" "DepartmentName"] + +---- 164 +SELECT +DepartmentID FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate +DepartmentID as "", +└Output field names [""] + +---- 165 +SELECT * FROM employee ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 166 +SELECT * FROM employee ORDER BY LastName DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order descending by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 167 +SELECT 1023 + DepartmentID AS y FROM employee ORDER BY y DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate 1023 + DepartmentID as "y", +└Output field names ["y"] +┌Order descending by y, +└Output field names ["y"] + +---- 168 +SELECT +DepartmentID AS y FROM employee ORDER BY y DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate +DepartmentID as "y", +└Output field names ["y"] +┌Order descending by y, +└Output field names ["y"] + +---- 169 +SELECT * FROM employee ORDER BY DepartmentID, LastName DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order descending by DepartmentID, LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 170 +SELECT * FROM employee ORDER BY 0 + DepartmentID DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order descending by 0 + DepartmentID, +└Output field names ["LastName" "DepartmentID"] + +---- 171 +SELECT * FROM employee ORDER BY +DepartmentID DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order descending by +DepartmentID, +└Output field names ["LastName" "DepartmentID"] + +---- 172 +SELECT ^DepartmentID AS y FROM employee ORDER BY y DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate ^DepartmentID as "y", +└Output field names ["y"] +┌Order descending by y, +└Output field names ["y"] + +---- 173 +SELECT ^uint8(DepartmentID) AS y FROM employee ORDER BY y DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate ^uint8(DepartmentID) as "y", +└Output field names ["y"] +┌Order descending by y, +└Output field names ["y"] + +---- 174 +SELECT * FROM t ORDER BY r; +┌Iterate all rows of table "t" +└Output field names ["r"] +┌Order by r, +└Output field names ["r"] + +---- 175 +SELECT i ^ 1 AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Evaluate i ^ 1 as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 176 +SELECT i | 1 AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Evaluate i | 1 as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 177 +SELECT i & 1 FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Evaluate i & 1 as "", +└Output field names [""] + +---- 178 +SELECT i &^ 1 AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Evaluate i &^ 1 as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 179 +SELECT * FROM employee WHERE LastName == "Jones" || DepartmentID IS NULL ORDER BY LastName DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Filter on LastName == "Jones" || DepartmentID IS NULL +└Output field names ["LastName" "DepartmentID"] +┌Order descending by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 180 +SELECT * FROM employee WHERE LastName != "Jones" && DepartmentID IS NOT NULL ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Filter on LastName != "Jones" && DepartmentID IS NOT NULL +│Possibly useful indices +│CREATE INDEX xemployee_LastName ON employee(LastName); +│CREATE INDEX xemployee_DepartmentID ON employee(DepartmentID); +└Output field names ["LastName" "DepartmentID"] +┌Order by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 185 +SELECT DepartmentID[0] FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate DepartmentID[0] as "", +└Output field names [""] + +---- 186 +SELECT "foo"[-DepartmentID] FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate "foo"[-DepartmentID] as "", +└Output field names [""] + +---- 187 +SELECT LastName[100] FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName[100] as "", +└Output field names [""] + +---- 188 +SELECT LastName[0], LastName FROM employee ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName[0] as "", LastName as "LastName", +└Output field names ["" "LastName"] +┌Order by LastName, +└Output field names ["" "LastName"] + +---- 189 +SELECT LastName, string(LastName[0]), string(LastName[1]), string(LastName[2]), string(LastName[3]) FROM employee ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "LastName", string(LastName[0]) as "", string(LastName[1]) as "", string(LastName[2]) as "", string(LastName[3]) as "", +└Output field names ["LastName" "" "" "" ""] +┌Order by LastName, +└Output field names ["LastName" "" "" "" ""] + +---- 190 +SELECT LastName, LastName[:], LastName[:2], LastName[2:], LastName[1:3] FROM employee ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "LastName", LastName[:] as "", LastName[:2] as "", LastName[2:] as "", LastName[1:3] as "", +└Output field names ["LastName" "" "" "" ""] +┌Order by LastName, +└Output field names ["LastName" "" "" "" ""] + +---- 192 +SELECT DepartmentID, LastName, LastName[:4], LastName[:0 * DepartmentID], LastName[0 * DepartmentID:0], LastName[0 * DepartmentID:0 * DepartmentID] FROM employee ORDER BY LastName DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate DepartmentID as "DepartmentID", LastName as "LastName", LastName[:4] as "", LastName[:0 * DepartmentID] as "", LastName[0 * DepartmentID:0] as "", LastName[0 * DepartmentID:0 * DepartmentID] as "", +└Output field names ["DepartmentID" "LastName" "" "" "" ""] +┌Order descending by LastName, +└Output field names ["DepartmentID" "LastName" "" "" "" ""] + +---- 193 +SELECT DepartmentID AS x, DepartmentID << 1 AS a, 1 << uint64(DepartmentID) AS b FROM employee WHERE DepartmentID IS NOT NULL ORDER BY x; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Filter on DepartmentID IS NOT NULL +│Possibly useful indices +│CREATE INDEX xemployee_DepartmentID ON employee(DepartmentID); +└Output field names ["LastName" "DepartmentID"] +┌Evaluate DepartmentID as "x", DepartmentID << 1 as "a", 1 << uint64(DepartmentID) as "b", +└Output field names ["x" "a" "b"] +┌Order by x, +└Output field names ["x" "a" "b"] + +---- 194 +SELECT DepartmentID AS x, DepartmentID >> 1 AS a, 9223372036854775808 >> uint64(DepartmentID) AS b FROM employee WHERE DepartmentID IS NOT NULL ORDER BY x; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Filter on DepartmentID IS NOT NULL +│Possibly useful indices +│CREATE INDEX xemployee_DepartmentID ON employee(DepartmentID); +└Output field names ["LastName" "DepartmentID"] +┌Evaluate DepartmentID as "x", DepartmentID >> 1 as "a", 9223372036854775808 >> uint64(DepartmentID) as "b", +└Output field names ["x" "a" "b"] +┌Order by x, +└Output field names ["x" "a" "b"] + +---- 195 +SELECT DISTINCT DepartmentID FROM employee WHERE DepartmentID IS NOT NULL; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Filter on DepartmentID IS NOT NULL +│Possibly useful indices +│CREATE INDEX xemployee_DepartmentID ON employee(DepartmentID); +└Output field names ["LastName" "DepartmentID"] +┌Evaluate DepartmentID as "DepartmentID", +└Output field names ["DepartmentID"] +┌Compute distinct rows +└Output field names [DepartmentID] + +---- 196 +SELECT DISTINCT e.DepartmentID, d.DepartmentID, e.LastName FROM employee AS e, department AS d WHERE e.DepartmentID == d.DepartmentID; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Evaluate e.DepartmentID as "e.DepartmentID", d.DepartmentID as "d.DepartmentID", e.LastName as "e.LastName", +└Output field names ["e.DepartmentID" "d.DepartmentID" "e.LastName"] +┌Compute distinct rows +└Output field names [e.DepartmentID d.DepartmentID e.LastName] + +---- 197 +SELECT DISTINCT e.DepartmentID, d.DepartmentID, e.LastName FROM employee AS e, department AS d WHERE e.DepartmentID == d.DepartmentID ORDER BY e.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Evaluate e.DepartmentID as "e.DepartmentID", d.DepartmentID as "d.DepartmentID", e.LastName as "e.LastName", +└Output field names ["e.DepartmentID" "d.DepartmentID" "e.LastName"] +┌Compute distinct rows +└Output field names [e.DepartmentID d.DepartmentID e.LastName] +┌Order by e.LastName, +└Output field names ["e.DepartmentID" "d.DepartmentID" "e.LastName"] + +---- 198 +SELECT * FROM employee, department ORDER BY employee.LastName, department.DepartmentID; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by employee.LastName, department.DepartmentID, +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 199 +SELECT * FROM employee, department WHERE employee.DepartmentID == department.DepartmentID ORDER BY employee.LastName, department.DepartmentID; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Filter on employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by employee.LastName, department.DepartmentID, +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 200 +SELECT * FROM department ORDER BY DepartmentID; +┌Iterate all rows of table "department" +└Output field names ["DepartmentID" "DepartmentName"] +┌Order by DepartmentID, +└Output field names ["DepartmentID" "DepartmentName"] + +---- 201 +SELECT * FROM department ORDER BY DepartmentID; +┌Iterate all rows of table "department" +└Output field names ["DepartmentID" "DepartmentName"] +┌Order by DepartmentID, +└Output field names ["DepartmentID" "DepartmentName"] + +---- 202 +SELECT * FROM department; +┌Iterate all rows of table "department" +└Output field names ["DepartmentID" "DepartmentName"] + +---- 203 +SELECT * FROM department ORDER BY DepartmentID; +┌Iterate all rows of table "department" +└Output field names ["DepartmentID" "DepartmentName"] +┌Order by DepartmentID, +└Output field names ["DepartmentID" "DepartmentName"] + +---- 204 +SELECT id(), LastName FROM employee ORDER BY id(); +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate id() as "", LastName as "LastName", +└Output field names ["" "LastName"] +┌Order by id(), +└Output field names ["" "LastName"] + +---- 205 +SELECT id(), LastName FROM employee ORDER BY id(); +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate id() as "", LastName as "LastName", +└Output field names ["" "LastName"] +┌Order by id(), +└Output field names ["" "LastName"] + +---- 206 +SELECT id(), LastName FROM employee ORDER BY id(); +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate id() as "", LastName as "LastName", +└Output field names ["" "LastName"] +┌Order by id(), +└Output field names ["" "LastName"] + +---- 207 +SELECT id(), e.LastName, e.DepartmentID, d.DepartmentID FROM employee AS e, department AS d WHERE e.DepartmentID == d.DepartmentID ORDER BY e.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Evaluate id() as "", e.LastName as "e.LastName", e.DepartmentID as "e.DepartmentID", d.DepartmentID as "d.DepartmentID", +└Output field names ["" "e.LastName" "e.DepartmentID" "d.DepartmentID"] +┌Order by e.LastName, +└Output field names ["" "e.LastName" "e.DepartmentID" "d.DepartmentID"] + +---- 208 +SELECT e.ID, e.LastName, e.DepartmentID, d.DepartmentID FROM (SELECT id() AS ID, LastName, DepartmentID FROM employee;) AS e, department AS d WHERE e.DepartmentID == d.DepartmentID ORDER BY e.ID; +┌Compute Cartesian product of +│ ┌Iterate all rows of virtual table "e" +│ │ ┌Iterate all rows of table "employee" +│ │ └Output field names ["LastName" "DepartmentID"] +│ │ ┌Evaluate id() as "ID", LastName as "LastName", DepartmentID as "DepartmentID", +│ │ └Output field names ["ID" "LastName" "DepartmentID"] +│ └Output field names ["ID" "LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +└Output field names ["e.ID" "e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Filter on e.DepartmentID == d.DepartmentID +└Output field names ["e.ID" "e.LastName" "e.DepartmentID" "d.DepartmentID" "d.DepartmentName"] +┌Evaluate e.ID as "e.ID", e.LastName as "e.LastName", e.DepartmentID as "e.DepartmentID", d.DepartmentID as "d.DepartmentID", +└Output field names ["e.ID" "e.LastName" "e.DepartmentID" "d.DepartmentID"] +┌Order by e.ID, +└Output field names ["e.ID" "e.LastName" "e.DepartmentID" "d.DepartmentID"] + +---- 209 +SELECT * FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] + +---- 210 +SELECT * FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] + +---- 211 +SELECT * FROM employee ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 212 +SELECT * FROM employee ORDER BY LastName DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order descending by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 213 +SELECT * FROM employee ORDER BY LastName DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order descending by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 214 +SELECT * FROM employee ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 215 +SELECT * FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] + +---- 216 +SELECT * FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] + +---- 217 +SELECT * FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] + +---- 223 +SELECT LastName[:len(LastName) - 3] AS y FROM employee ORDER BY y; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName[:len(LastName) - 3] as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 224 +SELECT complex(float32(DepartmentID + int64(id())), 0) AS x, complex(DepartmentID + int64(id()), 0) FROM employee ORDER BY real(x) DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate complex(float32(DepartmentID + int64(id())), 0) as "x", complex(DepartmentID + int64(id()), 0) as "", +└Output field names ["x" ""] +┌Order descending by real(x), +└Output field names ["x" ""] + +---- 225 +SELECT real(complex(float32(DepartmentID + int64(id())), 0)) AS x, real(complex(DepartmentID + int64(id()), 0)) FROM employee ORDER BY x DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate real(complex(float32(DepartmentID + int64(id())), 0)) as "x", real(complex(DepartmentID + int64(id()), 0)) as "", +└Output field names ["x" ""] +┌Order descending by x, +└Output field names ["x" ""] + +---- 226 +SELECT imag(complex(0, float32(DepartmentID + int64(id())))) AS x, imag(complex(0, DepartmentID + int64(id()))) FROM employee ORDER BY x DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Evaluate imag(complex(0, float32(DepartmentID + int64(id())))) as "x", imag(complex(0, DepartmentID + int64(id()))) as "", +└Output field names ["x" ""] +┌Order descending by x, +└Output field names ["x" ""] + +---- 227 +SELECT 100 * id(), c FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate 100 * id() as "", c as "c", +└Output field names ["" "c"] + +---- 229 +SELECT * FROM b; +┌Iterate all rows of table "b" +└Output field names ["b"] + +---- 230 +SELECT * FROM c; +┌Iterate all rows of table "c" +└Output field names ["c"] + +---- 231 +SELECT * FROM a; +┌Iterate all rows of table "a" +└Output field names ["a"] + +---- 233 +SELECT * FROM c; +┌Iterate all rows of table "c" +└Output field names ["c"] + +---- 234 +SELECT * FROM a; +┌Iterate all rows of table "a" +└Output field names ["a"] + +---- 235 +SELECT * FROM b; +┌Iterate all rows of table "b" +└Output field names ["b"] + +---- 237 +SELECT * FROM a, b; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "a" +│ └Output field names ["c"] +│ ┌Iterate all rows of table "b" +│ └Output field names ["d"] +└Output field names ["a.c" "b.d"] + +---- 238 +SELECT 9 * x2.c AS x2, 3 * x1.c AS x1, 1 * x0.c AS x0, 9 * x2.c + 3 * x1.c + x0.c AS y FROM a AS x2, a AS x1, a AS x0 ORDER BY y; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "a" +│ └Output field names ["c"] +│ ┌Iterate all rows of table "a" +│ └Output field names ["c"] +│ ┌Iterate all rows of table "a" +│ └Output field names ["c"] +└Output field names ["x2.c" "x1.c" "x0.c"] +┌Evaluate 9 * x2.c as "x2", 3 * x1.c as "x1", 1 * x0.c as "x0", 9 * x2.c + 3 * x1.c + x0.c as "y", +└Output field names ["x2" "x1" "x0" "y"] +┌Order by y, +└Output field names ["x2" "x1" "x0" "y"] + +---- 239 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 243 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 244 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 247 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["s"] + +---- 248 +SELECT i == 4294967280 FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Evaluate i == 4294967280 as "", +└Output field names [""] + +---- 249 +SELECT id() == 1 && s == "a" || id() == 2 && s == "�" && s == "�" || id() == 3 && s == "ø" && s == "ø" && s == "ø" || id() == 4 && s == "日" && s == "日" && s == "日" FROM t; +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Evaluate id() == 1 && s == "a" || id() == 2 && s == "�" && s == "�" || id() == 3 && s == "ø" && s == "ø" && s == "ø" || id() == 4 && s == "日" && s == "日" && s == "日" as "", +└Output field names [""] + +---- 250 +SELECT 3.3, 3.3 FROM t; +┌Iterate all rows of table "t" +└Output field names [] +┌Evaluate 3.3 as "", 3.3 as "", +└Output field names ["" ""] + +---- 252 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 253 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 255 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 257 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 260 +SELECT count() FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate count() as "", +└Output field names [""] + +---- 261 +SELECT count() AS y FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate count() as "y", +└Output field names ["y"] + +---- 262 +SELECT 3 * count() AS y FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate 3 * count() as "y", +└Output field names ["y"] + +---- 263 +SELECT count(LastName) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate count(LastName) as "", +└Output field names [""] + +---- 264 +SELECT count(DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate count(DepartmentID) as "", +└Output field names [""] + +---- 265 +SELECT count() - count(DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate count() - count(DepartmentID) as "", +└Output field names [""] + +---- 266 +SELECT min(LastName), min(DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate min(LastName) as "", min(DepartmentID) as "", +└Output field names ["" ""] + +---- 267 +SELECT max(LastName), max(DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate max(LastName) as "", max(DepartmentID) as "", +└Output field names ["" ""] + +---- 268 +SELECT sum(LastName), sum(DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate sum(LastName) as "", sum(DepartmentID) as "", +└Output field names ["" ""] + +---- 269 +SELECT sum(DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate sum(DepartmentID) as "", +└Output field names [""] + +---- 270 +SELECT avg(DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate avg(DepartmentID) as "", +└Output field names [""] + +---- 272 +SELECT DepartmentID, sum(DepartmentID) AS s FROM employee GROUP BY DepartmentID ORDER BY s DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by DepartmentID, +└Output field names ["LastName" "DepartmentID"] +┌Evaluate DepartmentID as "DepartmentID", sum(DepartmentID) as "s", +└Output field names ["DepartmentID" "s"] +┌Order descending by s, +└Output field names ["DepartmentID" "s"] + +---- 273 +SELECT DepartmentID, count(LastName + string(DepartmentID)) AS y FROM employee GROUP BY DepartmentID ORDER BY y DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by DepartmentID, +└Output field names ["LastName" "DepartmentID"] +┌Evaluate DepartmentID as "DepartmentID", count(LastName + string(DepartmentID)) as "y", +└Output field names ["DepartmentID" "y"] +┌Order descending by y, +└Output field names ["DepartmentID" "y"] + +---- 274 +SELECT DepartmentID, sum(2 * DepartmentID) AS s FROM employee GROUP BY DepartmentID ORDER BY s DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by DepartmentID, +└Output field names ["LastName" "DepartmentID"] +┌Evaluate DepartmentID as "DepartmentID", sum(2 * DepartmentID) as "s", +└Output field names ["DepartmentID" "s"] +┌Order descending by s, +└Output field names ["DepartmentID" "s"] + +---- 275 +SELECT min(2 * DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate min(2 * DepartmentID) as "", +└Output field names [""] + +---- 276 +SELECT max(2 * DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate max(2 * DepartmentID) as "", +└Output field names [""] + +---- 277 +SELECT avg(2 * DepartmentID) FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by distinct rows +└Output field names ["LastName" "DepartmentID"] +┌Evaluate avg(2 * DepartmentID) as "", +└Output field names [""] + +---- 278 +SELECT * FROM employee GROUP BY DepartmentID; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by DepartmentID, +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "LastName", DepartmentID as "DepartmentID", +└Output field names ["LastName" "DepartmentID"] + +---- 279 +SELECT * FROM employee GROUP BY DepartmentID ORDER BY LastName DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by DepartmentID, +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "LastName", DepartmentID as "DepartmentID", +└Output field names ["LastName" "DepartmentID"] +┌Order descending by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 280 +SELECT * FROM employee GROUP BY DepartmentID, LastName ORDER BY LastName DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by DepartmentID, LastName, +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "LastName", DepartmentID as "DepartmentID", +└Output field names ["LastName" "DepartmentID"] +┌Order descending by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 281 +SELECT * FROM employee GROUP BY LastName, DepartmentID ORDER BY LastName DESC; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Group by LastName, DepartmentID, +└Output field names ["LastName" "DepartmentID"] +┌Evaluate LastName as "LastName", DepartmentID as "DepartmentID", +└Output field names ["LastName" "DepartmentID"] +┌Order descending by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 282 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 283 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate count() as "", +└Output field names [""] + +---- 284 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate count() as "", +└Output field names [""] + +---- 285 +SELECT count() FROM t WHERE n < 2; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Filter on n < 2 +│Possibly useful indices +│CREATE INDEX xt_n ON t(n); +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate count() as "", +└Output field names [""] + +---- 286 +SELECT count() FROM t WHERE n < 1; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Filter on n < 1 +│Possibly useful indices +│CREATE INDEX xt_n ON t(n); +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate count() as "", +└Output field names [""] + +---- 287 +SELECT count() FROM t WHERE n < 0; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Filter on n < 0 +│Possibly useful indices +│CREATE INDEX xt_n ON t(n); +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate count() as "", +└Output field names [""] + +---- 288 +SELECT s + 10 FROM (SELECT sum(n) AS s FROM t WHERE n < 2;); +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Filter on n < 2 +│Possibly useful indices +│CREATE INDEX xt_n ON t(n); +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate sum(n) as "s", +└Output field names ["s"] +┌Evaluate s + 10 as "", +└Output field names [""] + +---- 289 +SELECT s + 10 FROM (SELECT sum(n) AS s FROM t WHERE n < 1;); +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Filter on n < 1 +│Possibly useful indices +│CREATE INDEX xt_n ON t(n); +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate sum(n) as "s", +└Output field names ["s"] +┌Evaluate s + 10 as "", +└Output field names [""] + +---- 290 +SELECT s + 10 FROM (SELECT sum(n) AS s FROM t WHERE n < 0;); +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Filter on n < 0 +│Possibly useful indices +│CREATE INDEX xt_n ON t(n); +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate sum(n) as "s", +└Output field names ["s"] +┌Evaluate s + 10 as "", +└Output field names [""] + +---- 291 +SELECT sum(n) AS s FROM t WHERE n < 2; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Filter on n < 2 +│Possibly useful indices +│CREATE INDEX xt_n ON t(n); +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate sum(n) as "s", +└Output field names ["s"] + +---- 292 +SELECT sum(n) AS s FROM t WHERE n < 1; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Filter on n < 1 +│Possibly useful indices +│CREATE INDEX xt_n ON t(n); +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate sum(n) as "s", +└Output field names ["s"] + +---- 293 +SELECT sum(n) AS s FROM t WHERE n < 0; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Filter on n < 0 +│Possibly useful indices +│CREATE INDEX xt_n ON t(n); +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate sum(n) as "s", +└Output field names ["s"] + +---- 294 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate count() as "", +└Output field names [""] + +---- 295 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate count() as "", +└Output field names [""] + +---- 296 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["n"] +┌Group by distinct rows +└Output field names ["n"] +┌Evaluate count() as "", +└Output field names [""] + +---- 297 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["S"] +┌Group by distinct rows +└Output field names ["S"] +┌Evaluate count() as "", +└Output field names [""] + +---- 298 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["S"] +┌Group by distinct rows +└Output field names ["S"] +┌Evaluate count() as "", +└Output field names [""] + +---- 299 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 300 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 301 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 302 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 303 +SELECT string(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate string(c) as "", +└Output field names [""] + +---- 304 +SELECT string(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate string(c) as "", +└Output field names [""] + +---- 305 +SELECT string(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate string(c) as "", +└Output field names [""] + +---- 306 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 307 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 308 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 309 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 310 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 311 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 312 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 313 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 314 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 315 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 316 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 317 +SELECT i, string(b) FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] +┌Evaluate i as "i", string(b) as "", +└Output field names ["i" ""] + +---- 318 +SELECT i, string(b) FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] +┌Evaluate i as "i", string(b) as "", +└Output field names ["i" ""] + +---- 319 +SELECT i, string(b) FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] +┌Evaluate i as "i", string(b) as "", +└Output field names ["i" ""] + +---- 320 +SELECT i, string(b) FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] +┌Evaluate i as "i", string(b) as "", +└Output field names ["i" ""] + +---- 321 +SELECT i, string(b) FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] +┌Evaluate i as "i", string(b) as "", +└Output field names ["i" ""] + +---- 322 +SELECT i, string(b) FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] +┌Evaluate i as "i", string(b) as "", +└Output field names ["i" ""] + +---- 323 +SELECT * FROM t ORDER BY true, c, false; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by true, c, false, +└Output field names ["c"] + +---- 324 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 325 +SELECT * FROM t ORDER BY 42, c, 24; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 326 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 327 +SELECT * FROM t ORDER BY 42, c, 24; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 328 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 329 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 330 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] + +---- 331 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 332 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 334 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 335 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 337 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 338 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 339 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 340 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 341 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 342 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 343 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 344 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 345 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 346 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 347 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 348 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 349 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 350 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 351 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 352 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 353 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 354 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 355 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 356 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 357 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 358 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 359 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 360 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 361 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 362 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 363 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 364 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 365 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 366 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 367 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] + +---- 368 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] + +---- 369 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] + +---- 370 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 371 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 372 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 373 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 374 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 375 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 376 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 377 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 378 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 379 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 380 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 381 +SELECT * FROM t WHERE c > 100 ORDER BY c DESC; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c > 100 +│Possibly useful indices +│CREATE INDEX xt_c ON t(c); +└Output field names ["c"] +┌Order descending by c, +└Output field names ["c"] + +---- 382 +SELECT * FROM t WHERE c < 110 ORDER BY c; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c < 110 +│Possibly useful indices +│CREATE INDEX xt_c ON t(c); +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 383 +SELECT * FROM t WHERE c <= 110 ORDER BY c; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c <= 110 +│Possibly useful indices +│CREATE INDEX xt_c ON t(c); +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 384 +SELECT * FROM t WHERE c >= 110 ORDER BY c; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c >= 110 +│Possibly useful indices +│CREATE INDEX xt_c ON t(c); +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 385 +SELECT * FROM t WHERE c != 110 ORDER BY c; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c != 110 +│Possibly useful indices +│CREATE INDEX xt_c ON t(c); +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 386 +SELECT * FROM t WHERE c == 110 ORDER BY c; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c == 110 +│Possibly useful indices +│CREATE INDEX xt_c ON t(c); +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 387 +SELECT c + 1000 AS s FROM t ORDER BY s; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate c + 1000 as "s", +└Output field names ["s"] +┌Order by s, +└Output field names ["s"] + +---- 388 +SELECT 1000 - c AS s FROM t ORDER BY s; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate 1000 - c as "s", +└Output field names ["s"] +┌Order by s, +└Output field names ["s"] + +---- 389 +SELECT c >> 1 AS s FROM t ORDER BY s; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate c >> 1 as "s", +└Output field names ["s"] +┌Order by s, +└Output field names ["s"] + +---- 390 +SELECT c << 1 AS s FROM t ORDER BY s; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate c << 1 as "s", +└Output field names ["s"] +┌Order by s, +└Output field names ["s"] + +---- 391 +SELECT * FROM t WHERE c & 349525 == 70992; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c & 349525 == 70992 +└Output field names ["c"] + +---- 392 +SELECT * FROM t WHERE c | 349525 == 358389; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c | 349525 == 358389 +└Output field names ["c"] + +---- 393 +SELECT * FROM t WHERE c &^ 349525 == 8864; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c &^ 349525 == 8864 +└Output field names ["c"] + +---- 394 +SELECT * FROM t WHERE c ^ 349525 == 287397; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c ^ 349525 == 287397 +└Output field names ["c"] + +---- 395 +SELECT * FROM t WHERE c % 256 == 240; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c % 256 == 240 +└Output field names ["c"] + +---- 396 +SELECT * FROM t WHERE c * 16 == 1277696; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on c * 16 == 1277696 +└Output field names ["c"] + +---- 397 +SELECT * FROM t WHERE ^c == -79857; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on ^c == -79857 +└Output field names ["c"] + +---- 398 +SELECT * FROM t WHERE +c == 79856; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on +c == 79856 +└Output field names ["c"] + +---- 399 +SELECT * FROM t WHERE -c == -79856; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Filter on -c == -79856 +└Output field names ["c"] + +---- 400 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 401 +SELECT c, sum(i) FROM t GROUP BY c; +┌Iterate all rows of table "t" +└Output field names ["c" "i"] +┌Group by c, +└Output field names ["c" "i"] +┌Evaluate c as "c", sum(i) as "", +└Output field names ["c" ""] + +---- 402 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 403 +SELECT * FROM t ORDER BY 15, c, 16; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 404 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 405 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 406 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 407 +SELECT c / d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c / d as "", +└Output field names [""] + +---- 408 +SELECT c % d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c % d as "", +└Output field names [""] + +---- 409 +SELECT c == d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c == d as "", +└Output field names [""] + +---- 410 +SELECT c == d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c == d as "", +└Output field names [""] + +---- 411 +SELECT c != d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c != d as "", +└Output field names [""] + +---- 412 +SELECT c != d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c != d as "", +└Output field names [""] + +---- 413 +SELECT c < d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c < d as "", +└Output field names [""] + +---- 414 +SELECT c < d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c < d as "", +└Output field names [""] + +---- 415 +SELECT c <= d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c <= d as "", +└Output field names [""] + +---- 416 +SELECT c <= d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c <= d as "", +└Output field names [""] + +---- 417 +SELECT c > d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c > d as "", +└Output field names [""] + +---- 418 +SELECT c > d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c > d as "", +└Output field names [""] + +---- 419 +SELECT c >= d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c >= d as "", +└Output field names [""] + +---- 420 +SELECT c >= d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c >= d as "", +└Output field names [""] + +---- 421 +SELECT c / d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c / d as "", +└Output field names [""] + +---- 422 +SELECT c / d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate c / d as "", +└Output field names [""] + +---- 424 +SELECT +c, -d FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate +c as "", -d as "", +└Output field names ["" ""] + +---- 425 +SELECT 1 + c, d + 1, 1.5 + c, d + 1.5 FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "d"] +┌Evaluate 1 + c as "", d + 1 as "", 1.5 + c as "", d + 1.5 as "", +└Output field names ["" "" "" ""] + +---- 426 +SELECT float64(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate float64(c) as "", +└Output field names [""] + +---- 427 +SELECT formatTime(timeIn(c, "UTC"), "2006-01-02 15:04:05.999999999 -0700") FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatTime(timeIn(c, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as "", +└Output field names [""] + +---- 428 +SELECT c, string(c) FROM t ORDER BY c; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate c as "c", string(c) as "", +└Output field names ["c" ""] +┌Order by c, +└Output field names ["c" ""] + +---- 429 +SELECT since(c) > duration("24h0m0s") FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate since(c) > duration("24h0m0s") as "", +└Output field names [""] + +---- 430 +SELECT since(c) >= duration("24h0m0s") FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate since(c) >= duration("24h0m0s") as "", +└Output field names [""] + +---- 431 +SELECT a > a, a > b, a > c, b > a, b > b, b > c, c > a, c > b, c > c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a > a as "", a > b as "", a > c as "", b > a as "", b > b as "", b > c as "", c > a as "", c > b as "", c > c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 432 +SELECT a < a, a < b, a < c, b < a, b < b, b < c, c < a, c < b, c < c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a < a as "", a < b as "", a < c as "", b < a as "", b < b as "", b < c as "", c < a as "", c < b as "", c < c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 433 +SELECT a <= a, a <= b, a <= c, b <= a, b <= b, b <= c, c <= a, c <= b, c <= c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a <= a as "", a <= b as "", a <= c as "", b <= a as "", b <= b as "", b <= c as "", c <= a as "", c <= b as "", c <= c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 434 +SELECT a >= a, a >= b, a >= c, b >= a, b >= b, b >= c, c >= a, c >= b, c >= c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a >= a as "", a >= b as "", a >= c as "", b >= a as "", b >= b as "", b >= c as "", c >= a as "", c >= b as "", c >= c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 435 +SELECT a != a, a != b, a != c, b != a, b != b, b != c, c != a, c != b, c != c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a != a as "", a != b as "", a != c as "", b != a as "", b != b as "", b != c as "", c != a as "", c != b as "", c != c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 436 +SELECT a == a, a == b, a == c, b == a, b == b, b == c, c == a, c == b, c == c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a == a as "", a == b as "", a == c as "", b == a as "", b == b as "", b == c as "", c == a as "", c == b as "", c == c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 437 +SELECT b + c, a + c, a + b, a + b + c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate b + c as "", a + c as "", a + b as "", a + b + c as "", +└Output field names ["" "" "" ""] + +---- 438 +SELECT b - c, a - c, a - b, a - b - c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate b - c as "", a - c as "", a - b as "", a - b - c as "", +└Output field names ["" "" "" ""] + +---- 439 +SELECT a >> 1, b >> 1, c >> 1 FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a >> 1 as "", b >> 1 as "", c >> 1 as "", +└Output field names ["" "" ""] + +---- 440 +SELECT a << 1, b << 1, c << 1 FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a << 1 as "", b << 1 as "", c << 1 as "", +└Output field names ["" "" ""] + +---- 441 +SELECT a & 255 FROM t; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate a & 255 as "", +└Output field names [""] + +---- 442 +SELECT a | 256 FROM t; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate a | 256 as "", +└Output field names [""] + +---- 443 +SELECT a &^ 3376 FROM t; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate a &^ 3376 as "", +└Output field names [""] + +---- 444 +SELECT a % duration("2h0m0s"), a % duration("1m0s") FROM t; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate a % duration("2h0m0s") as "", a % duration("1m0s") as "", +└Output field names ["" ""] + +---- 445 +SELECT a / 2, b / 2, c / 2 FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a / 2 as "", b / 2 as "", c / 2 as "", +└Output field names ["" "" ""] + +---- 446 +SELECT a * 2, 2 * b, c * 2 FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a * 2 as "", 2 * b as "", c * 2 as "", +└Output field names ["" "" ""] + +---- 447 +SELECT ^a, ^b, ^c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate ^a as "", ^b as "", ^c as "", +└Output field names ["" "" ""] + +---- 448 +SELECT +a, +b, +c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate +a as "", +b as "", +c as "", +└Output field names ["" "" ""] + +---- 449 +SELECT -a, -b, -c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate -a as "", -b as "", -c as "", +└Output field names ["" "" ""] + +---- 450 +SELECT a > a, a > b, a > c, b > a, b > b, b > c, c > a, c > b, c > c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a > a as "", a > b as "", a > c as "", b > a as "", b > b as "", b > c as "", c > a as "", c > b as "", c > c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 451 +SELECT a < a, a < b, a < c, b < a, b < b, b < c, c < a, c < b, c < c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a < a as "", a < b as "", a < c as "", b < a as "", b < b as "", b < c as "", c < a as "", c < b as "", c < c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 452 +SELECT a <= a, a <= b, a <= c, b <= a, b <= b, b <= c, c <= a, c <= b, c <= c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a <= a as "", a <= b as "", a <= c as "", b <= a as "", b <= b as "", b <= c as "", c <= a as "", c <= b as "", c <= c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 453 +SELECT a >= a, a >= b, a >= c, b >= a, b >= b, b >= c, c >= a, c >= b, c >= c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a >= a as "", a >= b as "", a >= c as "", b >= a as "", b >= b as "", b >= c as "", c >= a as "", c >= b as "", c >= c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 454 +SELECT a != a, a != b, a != c, b != a, b != b, b != c, c != a, c != b, c != c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a != a as "", a != b as "", a != c as "", b != a as "", b != b as "", b != c as "", c != a as "", c != b as "", c != c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 455 +SELECT a == a, a == b, a == c, b == a, b == b, b == c, c == a, c == b, c == c FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate a == a as "", a == b as "", a == c as "", b == a as "", b == b as "", b == c as "", c == a as "", c == b as "", c == c as "", +└Output field names ["" "" "" "" "" "" "" "" ""] + +---- 456 +SELECT formatTime(timeIn(a + b, "UTC"), "2006-01-02 15:04:05.999999999 -0700") FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b"] +┌Evaluate formatTime(timeIn(a + b, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as "", +└Output field names [""] + +---- 457 +SELECT formatTime(timeIn(b + a, "UTC"), "2006-01-02 15:04:05.999999999 -0700") FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b"] +┌Evaluate formatTime(timeIn(b + a, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as "", +└Output field names [""] + +---- 458 +SELECT a + a FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b"] +┌Evaluate a + a as "", +└Output field names [""] + +---- 459 +SELECT a - b FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b"] +┌Evaluate a - b as "", +└Output field names [""] + +---- 460 +SELECT formatTime(timeIn(a - b, "UTC"), "2006-01-02 15:04:05.999999999 -0700") FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b"] +┌Evaluate formatTime(timeIn(a - b, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as "", +└Output field names [""] + +---- 461 +SELECT b - a FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b"] +┌Evaluate b - a as "", +└Output field names [""] + +---- 462 +SELECT hours(a), minutes(a), seconds(a), nanoseconds(a) FROM t; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate hours(a) as "", minutes(a) as "", seconds(a) as "", nanoseconds(a) as "", +└Output field names ["" "" "" ""] + +---- 463 +SELECT a < now(), a < now(), a >= now(), a >= now() FROM t; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate a < now() as "", a < now() as "", a >= now() as "", a >= now() as "", +└Output field names ["" "" "" ""] + +---- 464 +SELECT formatTime(timeIn(a, "UTC"), "2006-01-02 15:04:05.999999999 -0700") AS a FROM t ORDER BY a; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate formatTime(timeIn(a, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as "a", +└Output field names ["a"] +┌Order by a, +└Output field names ["a"] + +---- 465 +SELECT hour(timeIn(a, "UTC")) AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate hour(timeIn(a, "UTC")) as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 466 +SELECT minute(a) AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate minute(a) as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 467 +SELECT second(a) AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate second(a) as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 468 +SELECT nanosecond(a) AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate nanosecond(a) as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 469 +SELECT year(a) AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate year(a) as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 470 +SELECT day(a) AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate day(a) as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 471 +SELECT month(a) AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate month(a) as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 472 +SELECT weekday(a) AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate weekday(a) as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 473 +SELECT yearDay(a) AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate yearDay(a) as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 474 +SELECT timeIn(a, ""), timeIn(a, "UTC") AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate timeIn(a, "") as "", timeIn(a, "UTC") as "y", +└Output field names ["" "y"] +┌Order by y, +└Output field names ["" "y"] + +---- 475 +SELECT formatTime(timeIn(a, "UTC"), "Jan 2, 2006 at 3:04pm (UTC)") AS y FROM t ORDER BY y; +┌Iterate all rows of table "t" +└Output field names ["a"] +┌Evaluate formatTime(timeIn(a, "UTC"), "Jan 2, 2006 at 3:04pm (UTC)") as "y", +└Output field names ["y"] +┌Order by y, +└Output field names ["y"] + +---- 478 +SELECT a, b, formatTime(timeIn(t, "UTC"), "2006-01-02 15:04:05.999999999 -0700") AS t FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "t"] +┌Evaluate a as "a", b as "b", formatTime(timeIn(t, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as "t", +└Output field names ["a" "b" "t"] + +---- 479 +SELECT a, b, formatTime(timeIn(t, "UTC"), "2006-01-02 15:04:05.999999999 -0700") AS t FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "t"] +┌Evaluate a as "a", b as "b", formatTime(timeIn(t, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as "t", +└Output field names ["a" "b" "t"] + +---- 480 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "d"] + +---- 481 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "d"] + +---- 482 +SELECT * FROM t ORDER BY real(c); +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by real(c), +└Output field names ["c"] + +---- 483 +SELECT id() AS i, contains(42, substr) FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["s" "substr"] +┌Evaluate id() as "i", contains(42, substr) as "", +└Output field names ["i" ""] +┌Order by i, +└Output field names ["i" ""] + +---- 484 +SELECT id() AS i, contains(s, true) FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["s" "substr"] +┌Evaluate id() as "i", contains(s, true) as "", +└Output field names ["i" ""] +┌Order by i, +└Output field names ["i" ""] + +---- 485 +SELECT id() AS i, contains(s, substr) FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["s" "substr"] +┌Evaluate id() as "i", contains(s, substr) as "", +└Output field names ["i" ""] +┌Order by i, +└Output field names ["i" ""] + +---- 486 +SELECT id() AS i, hasPrefix(42, prefix) FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["s" "prefix"] +┌Evaluate id() as "i", hasPrefix(42, prefix) as "", +└Output field names ["i" ""] +┌Order by i, +└Output field names ["i" ""] + +---- 487 +SELECT id() AS i, hasPrefix(s, false) FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["s" "prefix"] +┌Evaluate id() as "i", hasPrefix(s, false) as "", +└Output field names ["i" ""] +┌Order by i, +└Output field names ["i" ""] + +---- 488 +SELECT id() AS i, hasPrefix(s, prefix) FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["s" "prefix"] +┌Evaluate id() as "i", hasPrefix(s, prefix) as "", +└Output field names ["i" ""] +┌Order by i, +└Output field names ["i" ""] + +---- 489 +SELECT id() AS i, hasSuffix(42, suffix) FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["s" "suffix"] +┌Evaluate id() as "i", hasSuffix(42, suffix) as "", +└Output field names ["i" ""] +┌Order by i, +└Output field names ["i" ""] + +---- 490 +SELECT id() AS i, hasSuffix(s, true) FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["s" "suffix"] +┌Evaluate id() as "i", hasSuffix(s, true) as "", +└Output field names ["i" ""] +┌Order by i, +└Output field names ["i" ""] + +---- 491 +SELECT id() AS i, hasSuffix(s, suffix) FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["s" "suffix"] +┌Evaluate id() as "i", hasSuffix(s, suffix) as "", +└Output field names ["i" ""] +┌Order by i, +└Output field names ["i" ""] + +---- 493 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 495 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 496 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] + +---- 497 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 498 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 499 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["s"] + +---- 500 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 501 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 502 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "s"] + +---- 503 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "s"] + +---- 504 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c" "s"] + +---- 508 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 509 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 512 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 513 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 514 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 515 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 516 +SELECT * FROM t ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by id(), +└Output field names ["c"] + +---- 517 +SELECT * FROM t ORDER BY c; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Order by c, +└Output field names ["c"] + +---- 518 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 526 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 528 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 529 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 530 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 531 +SELECT * FROM t ORDER BY s; +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Order by s, +└Output field names ["s"] + +---- 532 +SELECT * FROM t ORDER BY s; +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Order by s, +└Output field names ["s"] + +---- 533 +SELECT * FROM t ORDER BY s; +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Order by s, +└Output field names ["s"] + +---- 534 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 535 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 536 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 537 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["s"] + +---- 538 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 539 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["s"] + +---- 540 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 541 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 542 +SELECT * FROM t ORDER BY s; +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Order by s, +└Output field names ["s"] + +---- 543 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 545 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 546 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 547 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] + +---- 548 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] +┌Order by i, +└Output field names ["i" "s"] + +---- 549 +SELECT * FROM t ORDER BY s; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] +┌Order by s, +└Output field names ["i" "s"] + +---- 550 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 551 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 552 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 553 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 554 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 555 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 556 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 557 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 558 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 559 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 560 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 561 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 562 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 563 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 564 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 565 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 566 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 569 +SELECT len(string(b)) AS n FROM t; +┌Iterate all rows of table "t" +└Output field names ["b"] +┌Evaluate len(string(b)) as "n", +└Output field names ["n"] + +---- 570 +SELECT len(string(b)) AS n FROM t; +┌Iterate all rows of table "t" +└Output field names ["b"] +┌Evaluate len(string(b)) as "n", +└Output field names ["n"] + +---- 571 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] + +---- 572 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] + +---- 574 +SELECT * FROM t AS u; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 577 +SELECT i FROM t AS u; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 578 +SELECT i FROM t WHERE b ORDER BY i; +┌Iterate all rows of table "t" using index "x" where b +└Output field names ["i" "b"] +┌Evaluate i as "i", +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 580 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 581 +SELECT i FROM t WHERE i < 30; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i < 30 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i"] + +---- 582 +SELECT i FROM t WHERE i < 30; +┌Iterate all rows of table "t" using index "x" where i < 30 +└Output field names ["i"] + +---- 583 +SELECT * FROM t WHERE i <= 30; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i <= 30 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i"] + +---- 584 +SELECT * FROM t WHERE i <= 30; +┌Iterate all rows of table "t" using index "x" where i <= 30 +└Output field names ["i"] + +---- 585 +SELECT i FROM t WHERE i == 30; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i == 30 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i"] + +---- 586 +SELECT i FROM t WHERE i == 30; +┌Iterate all rows of table "t" using index "x" where i == 30 +└Output field names ["i"] + +---- 587 +SELECT * FROM t WHERE i >= 30; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i >= 30 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i"] + +---- 588 +SELECT * FROM t WHERE i >= 30; +┌Iterate all rows of table "t" using index "x" where i >= 30 +└Output field names ["i"] + +---- 589 +SELECT * FROM t WHERE i > 30; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i > 30 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i"] + +---- 590 +SELECT * FROM t WHERE i > 30; +┌Iterate all rows of table "t" using index "x" where i > 30 +└Output field names ["i"] + +---- 591 +SELECT i FROM t WHERE !b ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] +┌Filter on !b +└Output field names ["i" "b"] +┌Evaluate i as "i", +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 592 +SELECT i FROM t WHERE !b ORDER BY i; +┌Iterate all rows of table "t" using index "x" where !b +└Output field names ["i" "b"] +┌Evaluate i as "i", +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 593 +SELECT i FROM t WHERE i < $1; +┌Iterate all rows of table "t" using index "x" where i < 30 +└Output field names ["i"] + +---- 594 +SELECT * FROM t WHERE i <= $1; +┌Iterate all rows of table "t" using index "x" where i <= 30 +└Output field names ["i"] + +---- 595 +SELECT i FROM t WHERE i == $1; +┌Iterate all rows of table "t" using index "x" where i == 30 +└Output field names ["i"] + +---- 596 +SELECT * FROM t WHERE i >= $1; +┌Iterate all rows of table "t" using index "x" where i >= 30 +└Output field names ["i"] + +---- 597 +SELECT * FROM t WHERE i > $1; +┌Iterate all rows of table "t" using index "x" where i > 30 +└Output field names ["i"] + +---- 598 +SELECT i FROM t WHERE i < $1; +┌Iterate all rows of table "t" using index "x" where i < 30 +└Output field names ["i"] + +---- 599 +SELECT * FROM t WHERE i <= $1; +┌Iterate all rows of table "t" using index "x" where i <= 30 +└Output field names ["i"] + +---- 600 +SELECT i FROM t WHERE i == $1; +┌Iterate all rows of table "t" using index "x" where i == 30 +└Output field names ["i"] + +---- 601 +SELECT * FROM t WHERE i >= $1; +┌Iterate all rows of table "t" using index "x" where i >= 30 +└Output field names ["i"] + +---- 602 +SELECT * FROM t WHERE i > $1; +┌Iterate all rows of table "t" using index "x" where i > 30 +└Output field names ["i"] + +---- 603 +SELECT i FROM t WHERE i < 30; +┌Iterate all rows of table "t" using index "x" where i < 30 +└Output field names ["i"] + +---- 604 +SELECT * FROM t WHERE i <= 30; +┌Iterate all rows of table "t" using index "x" where i <= 30 +└Output field names ["i"] + +---- 605 +SELECT i FROM t WHERE i == 30; +┌Iterate all rows of table "t" using index "x" where i == 30 +└Output field names ["i"] + +---- 606 +SELECT * FROM t WHERE i >= 30; +┌Iterate all rows of table "t" using index "x" where i >= 30 +└Output field names ["i"] + +---- 607 +SELECT * FROM t WHERE i > 30; +┌Iterate all rows of table "t" using index "x" where i > 30 +└Output field names ["i"] + +---- 608 +SELECT * FROM t WHERE i < 30; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i < 30 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i"] + +---- 609 +SELECT * FROM t WHERE i < 30; +┌Iterate all rows of table "t" using index "x" where i < 30 +└Output field names ["i"] + +---- 624 +SELECT * FROM __Table; +┌Iterate all rows of table "__Table" +└Output field names ["Name" "Schema"] + +---- 625 +SELECT * FROM __Table ORDER BY Name; +┌Iterate all rows of table "__Table" +└Output field names ["Name" "Schema"] +┌Order by Name, +└Output field names ["Name" "Schema"] + +---- 626 +SELECT * FROM __Table ORDER BY Name; +┌Iterate all rows of table "__Table" +└Output field names ["Name" "Schema"] +┌Order by Name, +└Output field names ["Name" "Schema"] + +---- 627 +SELECT * FROM __Column; +┌Iterate all rows of table "__Column" +└Output field names ["TableName" "Ordinal" "Name" "Type"] + +---- 628 +SELECT * FROM __Column ORDER BY TableName, Name; +┌Iterate all rows of table "__Column" +└Output field names ["TableName" "Ordinal" "Name" "Type"] +┌Order by TableName, Name, +└Output field names ["TableName" "Ordinal" "Name" "Type"] + +---- 629 +SELECT * FROM __Column ORDER BY TableName, Ordinal; +┌Iterate all rows of table "__Column" +└Output field names ["TableName" "Ordinal" "Name" "Type"] +┌Order by TableName, Ordinal, +└Output field names ["TableName" "Ordinal" "Name" "Type"] + +---- 630 +SELECT * FROM __Index; +┌Iterate all rows of table "__Index" +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] + +---- 631 +SELECT * FROM __Index ORDER BY TableName, Name; +┌Iterate all rows of table "__Index" +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] +┌Order by TableName, Name, +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] + +---- 632 +SELECT * FROM __Index WHERE !hasPrefix(TableName, "__") ORDER BY TableName, ColumnName, Name; +┌Iterate all rows of table "__Index" +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] +┌Order by TableName, ColumnName, Name, +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] + +---- 633 +SELECT * FROM __Index WHERE !hasPrefix(TableName, "__") ORDER BY TableName, ColumnName, Name; +┌Iterate all rows of table "__Index" +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] +┌Order by TableName, ColumnName, Name, +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] + +---- 634 +SELECT * FROM __Index WHERE !hasPrefix(TableName, "__") ORDER BY TableName, ColumnName, Name; +┌Iterate all rows of table "__Index" +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] +┌Order by TableName, ColumnName, Name, +└Output field names ["TableName" "ColumnName" "Name" "IsUnique"] + +---- 635 +SELECT c.TableName, c.Ordinal, c.Name FROM __Table AS t, __Column AS c WHERE t.Name == "u" && t.Name == c.TableName ORDER BY c.Ordinal; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "__Table" +│ └Output field names ["Name" "Schema"] +│ ┌Iterate all rows of table "__Column" +│ └Output field names ["TableName" "Ordinal" "Name" "Type"] +└Output field names ["t.Name" "t.Schema" "c.TableName" "c.Ordinal" "c.Name" "c.Type"] +┌Filter on t.Name == "u" && t.Name == c.TableName +└Output field names ["t.Name" "t.Schema" "c.TableName" "c.Ordinal" "c.Name" "c.Type"] +┌Evaluate c.TableName as "c.TableName", c.Ordinal as "c.Ordinal", c.Name as "c.Name", +└Output field names ["c.TableName" "c.Ordinal" "c.Name"] +┌Order by c.Ordinal, +└Output field names ["c.TableName" "c.Ordinal" "c.Name"] + +---- 636 +SELECT * FROM t WHERE s == "test"; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] +┌Filter on s == "test" +│Possibly useful indices +│CREATE INDEX xt_s ON t(s); +└Output field names ["i" "s"] + +---- 637 +SELECT * FROM t WHERE s == "test"; +┌Iterate all rows of table "t" using index "idx_s" where s == "test" +└Output field names ["i" "s"] + +---- 638 +SELECT * FROM __Table ORDER BY Name; +┌Iterate all rows of table "__Table" +└Output field names ["Name" "Schema"] +┌Order by Name, +└Output field names ["Name" "Schema"] + +---- 639 +SELECT * FROM __Table WHERE Name == "artist"; +┌Iterate all rows of table "__Table" +└Output field names ["Name" "Schema"] +┌Filter on Name == "artist" +└Output field names ["Name" "Schema"] + +---- 640 +SELECT * FROM __Column ORDER BY TableName, Ordinal; +┌Iterate all rows of table "__Column" +└Output field names ["TableName" "Ordinal" "Name" "Type"] +┌Order by TableName, Ordinal, +└Output field names ["TableName" "Ordinal" "Name" "Type"] + +---- 641 +SELECT * FROM __Column WHERE TableName == "artist" ORDER BY TableName, Ordinal; +┌Iterate all rows of table "__Column" +└Output field names ["TableName" "Ordinal" "Name" "Type"] +┌Filter on TableName == "artist" +└Output field names ["TableName" "Ordinal" "Name" "Type"] +┌Order by TableName, Ordinal, +└Output field names ["TableName" "Ordinal" "Name" "Type"] + +---- 642 +SELECT * FROM t, u WHERE u.y < 60 && t.k < 7; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "t" +│ └Output field names ["i" "j" "k"] +│ ┌Iterate all rows of table "u" +│ └Output field names ["x" "y" "z"] +└Output field names ["t.i" "t.j" "t.k" "u.x" "u.y" "u.z"] +┌Filter on u.y < 60 && t.k < 7 +│Possibly useful indices +│CREATE INDEX xu_y ON u(y); +│CREATE INDEX xt_k ON t(k); +└Output field names ["t.i" "t.j" "t.k" "u.x" "u.y" "u.z"] + +---- 643 +SELECT * FROM t, u WHERE u.y < 60 && t.k < 7; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "t" using index "xk" where k < 7 +│ └Output field names ["i" "j" "k"] +│ ┌Iterate all rows of table "u" +│ └Output field names ["x" "y" "z"] +└Output field names ["t.i" "t.j" "t.k" "u.x" "u.y" "u.z"] +┌Filter on u.y < 60 +│Possibly useful indices +│CREATE INDEX xu_y ON u(y); +└Output field names ["t.i" "t.j" "t.k" "u.x" "u.y" "u.z"] + +---- 644 +SELECT * FROM t, u WHERE u.y < 60 && t.k < 7; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "t" +│ └Output field names ["i" "j" "k"] +│ ┌Iterate all rows of table "u" using index "xy" where y < 60 +│ └Output field names ["x" "y" "z"] +└Output field names ["t.i" "t.j" "t.k" "u.x" "u.y" "u.z"] +┌Filter on t.k < 7 +│Possibly useful indices +│CREATE INDEX xt_k ON t(k); +└Output field names ["t.i" "t.j" "t.k" "u.x" "u.y" "u.z"] + +---- 645 +SELECT * FROM t, u WHERE u.y < 60 && t.k < 7; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "t" using index "xk" where k < 7 +│ └Output field names ["i" "j" "k"] +│ ┌Iterate all rows of table "u" using index "xy" where y < 60 +│ └Output field names ["x" "y" "z"] +└Output field names ["t.i" "t.j" "t.k" "u.x" "u.y" "u.z"] + +---- 646 +SELECT * FROM t OFFSET -1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Skip first -1 records +└Output field names ["i"] + +---- 647 +SELECT * FROM t OFFSET 0; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Skip first 0 records +└Output field names ["i"] + +---- 648 +SELECT * FROM t OFFSET 1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Skip first 1 records +└Output field names ["i"] + +---- 649 +SELECT * FROM t ORDER BY id() OFFSET -1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first -1 records +└Output field names ["i"] + +---- 650 +SELECT * FROM t ORDER BY id() OFFSET 0; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 0 records +└Output field names ["i"] + +---- 651 +SELECT * FROM t ORDER BY id() OFFSET 1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 1 records +└Output field names ["i"] + +---- 652 +SELECT * FROM t ORDER BY id() OFFSET 2; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 2 records +└Output field names ["i"] + +---- 653 +SELECT * FROM t ORDER BY id() LIMIT -1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Pass first -1 records +└Output field names [i] + +---- 654 +SELECT * FROM t ORDER BY id() LIMIT 0; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Pass first 0 records +└Output field names [i] + +---- 655 +SELECT * FROM t ORDER BY id() LIMIT 1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Pass first 1 records +└Output field names [i] + +---- 656 +SELECT * FROM t ORDER BY id() LIMIT -1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Pass first -1 records +└Output field names [i] + +---- 657 +SELECT * FROM t ORDER BY id() LIMIT 0; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Pass first 0 records +└Output field names [i] + +---- 658 +SELECT * FROM t ORDER BY id() LIMIT 1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Pass first 1 records +└Output field names [i] + +---- 659 +SELECT * FROM t ORDER BY id() LIMIT 2; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Pass first 2 records +└Output field names [i] + +---- 660 +SELECT * FROM t ORDER BY id() LIMIT 3; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Pass first 3 records +└Output field names [i] + +---- 661 +SELECT * FROM t ORDER BY id() LIMIT 0 OFFSET 0; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 0 records +└Output field names ["i"] +┌Pass first 0 records +└Output field names [i] + +---- 662 +SELECT * FROM t ORDER BY id() LIMIT 0 OFFSET 1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 1 records +└Output field names ["i"] +┌Pass first 0 records +└Output field names [i] + +---- 663 +SELECT * FROM t ORDER BY id() LIMIT 0 OFFSET 2; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 2 records +└Output field names ["i"] +┌Pass first 0 records +└Output field names [i] + +---- 664 +SELECT * FROM t ORDER BY id() LIMIT 0 OFFSET 3; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 3 records +└Output field names ["i"] +┌Pass first 0 records +└Output field names [i] + +---- 665 +SELECT * FROM t ORDER BY id() LIMIT 1 OFFSET 0; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 0 records +└Output field names ["i"] +┌Pass first 1 records +└Output field names [i] + +---- 666 +SELECT * FROM t ORDER BY id() LIMIT 1 OFFSET 1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 1 records +└Output field names ["i"] +┌Pass first 1 records +└Output field names [i] + +---- 667 +SELECT * FROM t ORDER BY id() LIMIT 1 OFFSET 2; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 2 records +└Output field names ["i"] +┌Pass first 1 records +└Output field names [i] + +---- 668 +SELECT * FROM t ORDER BY id() LIMIT 1 OFFSET 3; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 3 records +└Output field names ["i"] +┌Pass first 1 records +└Output field names [i] + +---- 669 +SELECT * FROM t ORDER BY id() LIMIT 2 OFFSET 0; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 0 records +└Output field names ["i"] +┌Pass first 2 records +└Output field names [i] + +---- 670 +SELECT * FROM t ORDER BY id() LIMIT 2 OFFSET 1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 1 records +└Output field names ["i"] +┌Pass first 2 records +└Output field names [i] + +---- 671 +SELECT * FROM t ORDER BY id() LIMIT 2 OFFSET 2; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 2 records +└Output field names ["i"] +┌Pass first 2 records +└Output field names [i] + +---- 672 +SELECT * FROM t ORDER BY id() LIMIT 2 OFFSET 3; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 3 records +└Output field names ["i"] +┌Pass first 2 records +└Output field names [i] + +---- 673 +SELECT * FROM t ORDER BY id() LIMIT 3 OFFSET 0; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 0 records +└Output field names ["i"] +┌Pass first 3 records +└Output field names [i] + +---- 674 +SELECT * FROM t ORDER BY id() LIMIT 3 OFFSET 1; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 1 records +└Output field names ["i"] +┌Pass first 3 records +└Output field names [i] + +---- 675 +SELECT * FROM t ORDER BY id() LIMIT 3 OFFSET 2; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 2 records +└Output field names ["i"] +┌Pass first 3 records +└Output field names [i] + +---- 676 +SELECT * FROM t ORDER BY id() LIMIT 3 OFFSET 3; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] +┌Skip first 3 records +└Output field names ["i"] +┌Pass first 3 records +└Output field names [i] + +---- 677 +SELECT * FROM (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, (SELECT * FROM u ORDER BY i;) AS b ORDER BY a.i, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of virtual table "a" +│ │ ┌Iterate all rows of table "t" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ │ ┌Skip first 1 records +│ │ └Output field names ["i"] +│ │ ┌Pass first 2 records +│ │ └Output field names [i] +│ └Output field names ["i"] +│ ┌Iterate all rows of virtual table "b" +│ │ ┌Iterate all rows of table "u" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ └Output field names ["i"] +└Output field names ["a.i" "b.i"] +┌Order by a.i, b.i, +└Output field names ["a.i" "b.i"] + +---- 678 +SELECT * FROM (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, (SELECT * FROM u ORDER BY i OFFSET 1;) AS b ORDER BY a.i, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of virtual table "a" +│ │ ┌Iterate all rows of table "t" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ │ ┌Skip first 1 records +│ │ └Output field names ["i"] +│ │ ┌Pass first 2 records +│ │ └Output field names [i] +│ └Output field names ["i"] +│ ┌Iterate all rows of virtual table "b" +│ │ ┌Iterate all rows of table "u" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ │ ┌Skip first 1 records +│ │ └Output field names ["i"] +│ └Output field names ["i"] +└Output field names ["a.i" "b.i"] +┌Order by a.i, b.i, +└Output field names ["a.i" "b.i"] + +---- 679 +SELECT * FROM (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, (SELECT * FROM u ORDER BY i LIMIT 1;) AS b ORDER BY a.i, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of virtual table "a" +│ │ ┌Iterate all rows of table "t" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ │ ┌Skip first 1 records +│ │ └Output field names ["i"] +│ │ ┌Pass first 2 records +│ │ └Output field names [i] +│ └Output field names ["i"] +│ ┌Iterate all rows of virtual table "b" +│ │ ┌Iterate all rows of table "u" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ │ ┌Pass first 1 records +│ │ └Output field names [i] +│ └Output field names ["i"] +└Output field names ["a.i" "b.i"] +┌Order by a.i, b.i, +└Output field names ["a.i" "b.i"] + +---- 680 +SELECT * FROM (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, (SELECT * FROM u ORDER BY i LIMIT 1 OFFSET 1;) AS b ORDER BY a.i, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of virtual table "a" +│ │ ┌Iterate all rows of table "t" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ │ ┌Skip first 1 records +│ │ └Output field names ["i"] +│ │ ┌Pass first 2 records +│ │ └Output field names [i] +│ └Output field names ["i"] +│ ┌Iterate all rows of virtual table "b" +│ │ ┌Iterate all rows of table "u" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ │ ┌Skip first 1 records +│ │ └Output field names ["i"] +│ │ ┌Pass first 1 records +│ │ └Output field names [i] +│ └Output field names ["i"] +└Output field names ["a.i" "b.i"] +┌Order by a.i, b.i, +└Output field names ["a.i" "b.i"] + +---- 681 +SELECT * FROM (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, (SELECT * FROM u ORDER BY i LIMIT 1 OFFSET 1;) AS b ORDER BY a.i, b.i LIMIT 1; +┌Compute Cartesian product of +│ ┌Iterate all rows of virtual table "a" +│ │ ┌Iterate all rows of table "t" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ │ ┌Skip first 1 records +│ │ └Output field names ["i"] +│ │ ┌Pass first 2 records +│ │ └Output field names [i] +│ └Output field names ["i"] +│ ┌Iterate all rows of virtual table "b" +│ │ ┌Iterate all rows of table "u" +│ │ └Output field names ["i"] +│ │ ┌Order by i, +│ │ └Output field names ["i"] +│ │ ┌Skip first 1 records +│ │ └Output field names ["i"] +│ │ ┌Pass first 1 records +│ │ └Output field names [i] +│ └Output field names ["i"] +└Output field names ["a.i" "b.i"] +┌Order by a.i, b.i, +└Output field names ["a.i" "b.i"] +┌Pass first 1 records +└Output field names [a.i b.i] + +---- 682 +SELECT count(1) AS total FROM fibonacci WHERE input >= 5 && input <= 7 || input == 3; +┌Iterate all rows of table "fibonacci" +└Output field names ["input" "output"] +┌Filter on input >= 5 && input <= 7 || input == 3 +└Output field names ["input" "output"] +┌Group by distinct rows +└Output field names ["input" "output"] +┌Evaluate count(1) as "total", +└Output field names ["total"] + +---- 683 +SELECT * FROM fibonacci WHERE input >= 5 && input <= 7 || input == 3 ORDER BY input DESC LIMIT 2 OFFSET 1; +┌Iterate all rows of table "fibonacci" +└Output field names ["input" "output"] +┌Filter on input >= 5 && input <= 7 || input == 3 +└Output field names ["input" "output"] +┌Order descending by input, +└Output field names ["input" "output"] +┌Skip first 1 records +└Output field names ["input" "output"] +┌Pass first 2 records +└Output field names [input output] + +---- 684 +SELECT * FROM fibonacci ORDER BY input; +┌Iterate all rows of table "fibonacci" +└Output field names ["input" "output"] +┌Order by input, +└Output field names ["input" "output"] + +---- 685 +SELECT * FROM fibonacci ORDER BY input; +┌Iterate all rows of table "fibonacci" +└Output field names ["input" "output"] +┌Order by input, +└Output field names ["input" "output"] + +---- 686 +SELECT count() AS total FROM fibonacci WHERE input >= 5 && input <= 7 || input == 3; +┌Iterate all rows of table "fibonacci" +└Output field names ["input" "output"] +┌Filter on input >= 5 && input <= 7 || input == 3 +└Output field names ["input" "output"] +┌Group by distinct rows +└Output field names ["input" "output"] +┌Evaluate count() as "total", +└Output field names ["total"] + +---- 687 +SELECT count() AS total FROM fibonacci WHERE input >= 5 && input <= 7 || input == 3; +┌Iterate all rows of table "fibonacci" +└Output field names ["input" "output"] +┌Filter on input >= 5 && input <= 7 || input == 3 +└Output field names ["input" "output"] +┌Group by distinct rows +└Output field names ["input" "output"] +┌Evaluate count() as "total", +└Output field names ["total"] + +---- 688 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 689 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 690 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 691 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 692 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 693 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 694 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 695 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 696 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 697 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 698 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 699 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 700 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 701 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 702 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 703 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 704 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 705 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 706 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 707 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 708 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 709 +SELECT * FROM t ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 711 +SELECT s FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] +┌Evaluate s as "s", +└Output field names ["s"] + +---- 712 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 714 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 715 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 716 +SELECT s FROM t WHERE s != "z"; +┌Iterate all rows of table "t" using index "x" where s != "z" +└Output field names ["i" "s"] +┌Evaluate s as "s", +└Output field names ["s"] + +---- 717 +SELECT s FROM t WHERE s < "z"; +┌Iterate all rows of table "t" using index "x" where s < "z" +└Output field names ["i" "s"] +┌Evaluate s as "s", +└Output field names ["s"] + +---- 718 +SELECT s FROM t WHERE s < "z"; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] +┌Filter on s < "z" +│Possibly useful indices +│CREATE INDEX xt_s ON t(s); +└Output field names ["i" "s"] +┌Evaluate s as "s", +└Output field names ["s"] + +---- 721 +SELECT s FROM t WHERE s < "z"; +┌Iterate all rows of table "t" +└Output field names ["i" "s"] +┌Filter on s < "z" +│Possibly useful indices +│CREATE INDEX xt_s ON t(s); +└Output field names ["i" "s"] +┌Evaluate s as "s", +└Output field names ["s"] + +---- 722 +SELECT p, string(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["p" "c"] +┌Evaluate p as "p", string(c) as "", +└Output field names ["p" ""] + +---- 723 +SELECT p, string(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["p" "c"] +┌Evaluate p as "p", string(c) as "", +└Output field names ["p" ""] + +---- 724 +SELECT p, string(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["p" "c"] +┌Evaluate p as "p", string(c) as "", +└Output field names ["p" ""] + +---- 725 +SELECT * FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] + +---- 726 +SELECT * FROM employee; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] + +---- 727 +SELECT * FROM employee ORDER BY LastName; +┌Iterate all rows of table "employee" +└Output field names ["LastName" "DepartmentID"] +┌Order by LastName, +└Output field names ["LastName" "DepartmentID"] + +---- 728 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["username" "departname" "created" "detail_id" "height" "avatar" "is_man"] + +---- 729 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["username" "departname" "created" "detail_id" "height" "avatar" "is_man"] + +---- 730 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["username" "departname" "created" "detail_id" "height" "avatar" "is_man"] + +---- 731 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["username" "departname" "created" "detail_id" "height" "avatar" "is_man"] + +---- 732 +SELECT id() IN (SELECT id() FROM t WHERE username == "2xiaolunwen";), username == "2xiaolunwen", len(string(avatar)) == 2097152 FROM t; +┌Iterate all rows of table "t" +└Output field names ["username" "departname" "created" "detail_id" "height" "avatar" "is_man"] +┌Evaluate id() IN (SELECT id() FROM t WHERE username == "2xiaolunwen";) as "", username == "2xiaolunwen" as "", len(string(avatar)) == 2097152 as "", +└Output field names ["" "" ""] + +---- 733 +SELECT id() IN (SELECT id() FROM t WHERE username == "xiaolunwen";), username == "xiaolunwen", len(string(avatar)) == 1048576 FROM t; +┌Iterate all rows of table "t" +└Output field names ["username" "departname" "created" "detail_id" "height" "avatar" "is_man"] +┌Evaluate id() IN (SELECT id() FROM t WHERE username == "xiaolunwen";) as "", username == "xiaolunwen" as "", len(string(avatar)) == 1048576 as "", +└Output field names ["" "" ""] + +---- 734 +SELECT user, remain, total FROM no_id_user WHERE user == "xlw" LIMIT 1; +┌Iterate all rows of table "no_id_user" using index "UQE_no_id_user_user" where user == "xlw" +└Output field names ["user" "remain" "total"] +┌Pass first 1 records +└Output field names [user remain total] + +---- 735 +SELECT * FROM t WHERE id() < 4; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on id() < 4 +│Possibly useful indices +│CREATE INDEX xt_id ON t(id()); +└Output field names ["i"] + +---- 736 +SELECT * FROM t WHERE i < 4; +┌Iterate all rows of table "t" using index "x" where i < 4 +└Output field names ["i"] + +---- 737 +SELECT * FROM t WHERE i <= 4; +┌Iterate all rows of table "t" using index "x" where i <= 4 +└Output field names ["i"] + +---- 738 +SELECT * FROM t WHERE i == 4; +┌Iterate all rows of table "t" using index "x" where i == 4 +└Output field names ["i"] + +---- 739 +SELECT * FROM t WHERE i >= 4; +┌Iterate all rows of table "t" using index "x" where i >= 4 +└Output field names ["i"] + +---- 740 +SELECT * FROM t WHERE i > 4; +┌Iterate all rows of table "t" using index "x" where i > 4 +└Output field names ["i"] + +---- 741 +SELECT * FROM (SELECT i FROM t WHERE i < 4;) AS t, (SELECT * FROM u WHERE i < 40;) AS u; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "t" using index "x" where i < 4 +│ └Output field names ["i"] +│ ┌Iterate all rows of table "u" using index "y" where i < 40 +│ └Output field names ["i"] +└Output field names ["t.i" "u.i"] + +---- 742 +SELECT max(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate max(t) as "T", +└Output field names ["T"] + +---- 743 +SELECT max(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate max(t) as "T", +└Output field names ["T"] + +---- 744 +SELECT max(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate max(t) as "T", +└Output field names ["T"] + +---- 745 +SELECT max(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate max(t) as "T", +└Output field names ["T"] + +---- 746 +SELECT max(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate max(t) as "T", +└Output field names ["T"] + +---- 747 +SELECT max(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate max(t) as "T", +└Output field names ["T"] + +---- 748 +SELECT min(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate min(t) as "T", +└Output field names ["T"] + +---- 749 +SELECT min(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate min(t) as "T", +└Output field names ["T"] + +---- 750 +SELECT min(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate min(t) as "T", +└Output field names ["T"] + +---- 751 +SELECT min(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate min(t) as "T", +└Output field names ["T"] + +---- 752 +SELECT min(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate min(t) as "T", +└Output field names ["T"] + +---- 753 +SELECT min(t) AS T FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate min(t) as "T", +└Output field names ["T"] + +---- 754 +SELECT * FROM department; +┌Iterate all rows of table "department" +└Output field names ["Name" "score"] + +SELECT * FROM department; +┌Iterate all rows of table "department" +└Output field names ["Name" "score"] + +SELECT * FROM department ORDER BY Name; +┌Iterate all rows of table "department" +└Output field names ["Name" "score"] +┌Order by Name, +└Output field names ["Name" "score"] + +---- 755 +SELECT id(), s FROM t WHERE s LIKE "foo" ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on s LIKE "foo" +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 756 +SELECT id(), s FROM t WHERE !s LIKE "foo" ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on !s LIKE "foo" +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 757 +SELECT id(), s FROM t WHERE s LIKE "foo" IS NULL ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on s LIKE "foo" IS NULL +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 758 +SELECT id(), s FROM t WHERE s LIKE "foo" IS NOT NULL ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on s LIKE "foo" IS NOT NULL +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 759 +SELECT id(), s FROM t WHERE s LIKE "bar" ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on s LIKE "bar" +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 760 +SELECT id(), s FROM t WHERE s LIKE "^bar" ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on s LIKE "^bar" +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 761 +SELECT id(), s FROM t WHERE s LIKE "bar$" ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on s LIKE "bar$" +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 762 +SELECT id(), s FROM t WHERE s LIKE "bar$" ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on s LIKE "bar$" +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 763 +SELECT id(), s FROM t WHERE s + "qux" LIKE "qux$" ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on s + "qux" LIKE "qux$" +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 764 +SELECT id(), s FROM t WHERE s + "quxx" LIKE "qux$" ORDER BY id(); +┌Iterate all rows of table "t" +└Output field names ["s"] +┌Filter on s + "quxx" LIKE "qux$" +└Output field names ["s"] +┌Evaluate id() as "", s as "s", +└Output field names ["" "s"] +┌Order by id(), +└Output field names ["" "s"] + +---- 765 +SELECT * FROM (SELECT id() AS ID, i FROM foo;) AS foo, bar WHERE bar.fooID == foo.ID ORDER BY foo.ID; +┌Compute Cartesian product of +│ ┌Iterate all rows of virtual table "foo" +│ │ ┌Iterate all rows of table "foo" +│ │ └Output field names ["i"] +│ │ ┌Evaluate id() as "ID", i as "i", +│ │ └Output field names ["ID" "i"] +│ └Output field names ["ID" "i"] +│ ┌Iterate all rows of table "bar" +│ └Output field names ["fooID" "s"] +└Output field names ["foo.ID" "foo.i" "bar.fooID" "bar.s"] +┌Filter on bar.fooID == foo.ID +└Output field names ["foo.ID" "foo.i" "bar.fooID" "bar.s"] +┌Order by foo.ID, +└Output field names ["foo.ID" "foo.i" "bar.fooID" "bar.s"] + +---- 766 +SELECT * FROM foo, bar WHERE bar.fooID == id(foo) ORDER BY id(foo); +┌Compute Cartesian product of +│ ┌Iterate all rows of table "foo" +│ └Output field names ["i"] +│ ┌Iterate all rows of table "bar" +│ └Output field names ["fooID" "s"] +└Output field names ["foo.i" "bar.fooID" "bar.s"] +┌Filter on bar.fooID == id(foo) +└Output field names ["foo.i" "bar.fooID" "bar.s"] +┌Order by id(foo), +└Output field names ["foo.i" "bar.fooID" "bar.s"] + +---- 767 +SELECT * FROM t WHERE name == "b" && mail == "bar@example.com"; +┌Iterate all rows of table "t" +└Output field names ["name" "mail"] +┌Filter on name == "b" && mail == "bar@example.com" +│Possibly useful indices +│CREATE INDEX xt_name ON t(name); +│CREATE INDEX xt_mail ON t(mail); +└Output field names ["name" "mail"] + +---- 768 +SELECT * FROM t WHERE name == "b" && mail == "bar@example.com"; +┌Iterate all rows of table "t" +└Output field names ["name" "mail"] +┌Filter on name == "b" && mail == "bar@example.com" +│Possibly useful indices +│CREATE INDEX xt_name ON t(name); +│CREATE INDEX xt_mail ON t(mail); +└Output field names ["name" "mail"] + +---- 769 +SELECT * FROM t WHERE name == "b" || mail == "bar@example.com" ORDER BY name; +┌Iterate all rows of table "t" +└Output field names ["name" "mail"] +┌Filter on name == "b" || mail == "bar@example.com" +└Output field names ["name" "mail"] +┌Order by name, +└Output field names ["name" "mail"] + +---- 770 +SELECT * FROM t WHERE name == "b" || mail == "bar@example.com" ORDER BY name; +┌Iterate all rows of table "t" +└Output field names ["name" "mail"] +┌Filter on name == "b" || mail == "bar@example.com" +└Output field names ["name" "mail"] +┌Order by name, +└Output field names ["name" "mail"] + +---- 771 +SELECT id(), i FROM tableA WHERE id() IN (SELECT idA FROM tableB;) ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Filter on id() IN (SELECT idA FROM tableB;) +└Output field names ["i"] +┌Evaluate id() as "", i as "i", +└Output field names ["" "i"] +┌Order by id(), +└Output field names ["" "i"] + +---- 772 +SELECT id(), i FROM tableA WHERE id() NOT IN (SELECT idA FROM tableB;) ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Filter on id() NOT IN (SELECT idA FROM tableB;) +└Output field names ["i"] +┌Evaluate id() as "", i as "i", +└Output field names ["" "i"] +┌Order by id(), +└Output field names ["" "i"] + +---- 773 +SELECT id(), i FROM tableA ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Evaluate id() as "", i as "i", +└Output field names ["" "i"] +┌Order by id(), +└Output field names ["" "i"] + +---- 774 +SELECT id(), i FROM tableA ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Evaluate id() as "", i as "i", +└Output field names ["" "i"] +┌Order by id(), +└Output field names ["" "i"] + +---- 775 +SELECT id(), i FROM tableA ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Evaluate id() as "", i as "i", +└Output field names ["" "i"] +┌Order by id(), +└Output field names ["" "i"] + +---- 776 +SELECT id(), i FROM tableA ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Evaluate id() as "", i as "i", +└Output field names ["" "i"] +┌Order by id(), +└Output field names ["" "i"] + +---- 777 +SELECT id(), i FROM tableA ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Evaluate id() as "", i as "i", +└Output field names ["" "i"] +┌Order by id(), +└Output field names ["" "i"] + +---- 778 +SELECT id(), i FROM tableA ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Evaluate id() as "", i as "i", +└Output field names ["" "i"] +┌Order by id(), +└Output field names ["" "i"] + +---- 781 +SELECT i FROM tableA WHERE id() IN (SELECT idA FROM tableB;) ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Filter on id() IN (SELECT idA FROM tableB;) +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] + +---- 782 +SELECT i FROM tableA WHERE id() IN (SELECT idA FROM tableB;) ORDER BY id(); +┌Iterate all rows of table "tableA" +└Output field names ["i"] +┌Filter on id() IN (SELECT idA FROM tableB;) +└Output field names ["i"] +┌Order by id(), +└Output field names ["i"] + +---- 783 +SELECT * FROM testA; +┌Iterate all rows of table "testA" +└Output field names ["comment" "data"] + +---- 784 +SELECT * FROM testA; +┌Iterate all rows of table "testA" +└Output field names ["comment" "data"] + +---- 785 +SELECT * FROM testA ORDER BY comment; +┌Iterate all rows of table "testA" +└Output field names ["comment" "data"] +┌Order by comment, +└Output field names ["comment" "data"] + +---- 786 +SELECT * FROM testA ORDER BY comment; +┌Iterate all rows of table "testA" +└Output field names ["comment" "data"] +┌Order by comment, +└Output field names ["comment" "data"] + +---- 787 +SELECT * FROM testA ORDER BY comment; +┌Iterate all rows of table "testA" +└Output field names ["comment" "data"] +┌Order by comment, +└Output field names ["comment" "data"] + +---- 788 +SELECT * FROM testA ORDER BY comment; +┌Iterate all rows of table "testA" +└Output field names ["comment" "data"] +┌Order by comment, +└Output field names ["comment" "data"] + +---- 789 +SELECT * FROM testA ORDER BY comment; +┌Iterate all rows of table "testA" +└Output field names ["comment" "data"] +┌Order by comment, +└Output field names ["comment" "data"] + +---- 790 +SELECT * FROM testA ORDER BY comment; +┌Iterate all rows of table "testA" +└Output field names ["comment" "data"] +┌Order by comment, +└Output field names ["comment" "data"] + +---- 791 +SELECT formatFloat(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatFloat(c) as "", +└Output field names [""] + +---- 792 +SELECT formatFloat(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatFloat(c) as "", +└Output field names [""] + +---- 793 +SELECT formatFloat(c, 98) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatFloat(c, 98) as "", +└Output field names [""] + +---- 794 +SELECT formatFloat(c, 101, 5) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatFloat(c, 101, 5) as "", +└Output field names [""] + +---- 795 +SELECT formatFloat(c, 98, 7, 32) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatFloat(c, 98, 7, 32) as "", +└Output field names [""] + +---- 796 +SELECT formatInt(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c) as "", +└Output field names [""] + +---- 797 +SELECT formatInt(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c) as "", +└Output field names [""] + +---- 798 +SELECT formatInt(c, 18) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c, 18) as "", +└Output field names [""] + +---- 799 +SELECT formatInt(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c) as "", +└Output field names [""] + +---- 800 +SELECT formatInt(c) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c) as "", +└Output field names [""] + +---- 801 +SELECT formatInt(c, 18) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c, 18) as "", +└Output field names [""] + +---- 802 +SELECT formatInt(c, 18) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c, 18) as "", +└Output field names [""] + +---- 803 +SELECT formatInt(c, 18) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c, 18) as "", +└Output field names [""] + +---- 804 +SELECT formatInt(c, 18) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c, 18) as "", +└Output field names [""] + +---- 805 +SELECT formatInt(c, 18) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c, 18) as "", +└Output field names [""] + +---- 806 +SELECT formatInt(c, 18) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c, 18) as "", +└Output field names [""] + +---- 807 +SELECT formatInt(c, 18) FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] +┌Evaluate formatInt(c, 18) as "", +└Output field names [""] + +---- 808 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 809 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i" "b"] + +---- 815 +SELECT * FROM __Column2; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 816 +SELECT * FROM __Column2; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 817 +SELECT * FROM __Column2; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 818 +SELECT * FROM __Column2; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 819 +SELECT * FROM __Column2; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 822 +SELECT * FROM __Column2; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 823 +SELECT * FROM __Column2; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 824 +SELECT * FROM __Column2; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 825 +SELECT * FROM __Column2; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 830 +SELECT * FROM __Column2 ORDER BY Name; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] +┌Order by Name, +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 831 +SELECT * FROM __Column2 ORDER BY Name; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] +┌Order by Name, +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 832 +SELECT * FROM __Column2 ORDER BY Name; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] +┌Order by Name, +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 833 +SELECT * FROM __Column2 ORDER BY Name; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] +┌Order by Name, +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 834 +SELECT * FROM __Column2 ORDER BY Name; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] +┌Order by Name, +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 835 +SELECT * FROM __Column2 ORDER BY Name; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] +┌Order by Name, +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 836 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 837 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 839 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 842 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 844 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 846 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 847 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 850 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 854 +SELECT * FROM department; +┌Iterate all rows of table "department" +└Output field names ["DepartmentName"] + +---- 855 +SELECT * FROM department; +┌Iterate all rows of table "department" +└Output field names ["DepartmentName"] + +---- 857 +SELECT * FROM department; +┌Iterate all rows of table "department" +└Output field names ["DepartmentName"] + +---- 858 +SELECT TimeStamp IS NOT NULL FROM t; +┌Iterate all rows of table "t" +└Output field names ["TimeStamp"] +┌Evaluate TimeStamp IS NOT NULL as "", +└Output field names [""] + +---- 861 +SELECT TimeStamp IS NOT NULL FROM t; +┌Iterate all rows of table "t" +└Output field names ["TimeStamp"] +┌Evaluate TimeStamp IS NOT NULL as "", +└Output field names [""] + +---- 862 +SELECT TimeStamp IS NOT NULL FROM t; +┌Iterate all rows of table "t" +└Output field names ["TimeStamp"] +┌Evaluate TimeStamp IS NOT NULL as "", +└Output field names [""] + +---- 864 +SELECT Event FROM t; +┌Iterate all rows of table "t" +└Output field names ["Event"] + +---- 865 +SELECT b FROM t ORDER BY b DESC; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate b as "b", +└Output field names ["b"] +┌Order descending by b, +└Output field names ["b"] + +---- 866 +SELECT b FROM t ORDER BY b DESC; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate b as "b", +└Output field names ["b"] +┌Order descending by b, +└Output field names ["b"] + +---- 868 +SELECT b FROM t ORDER BY b DESC; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate b as "b", +└Output field names ["b"] +┌Order descending by b, +└Output field names ["b"] + +---- 870 +SELECT b FROM t ORDER BY b DESC; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] +┌Evaluate b as "b", +└Output field names ["b"] +┌Order descending by b, +└Output field names ["b"] + +---- 871 +SELECT * FROM employee LEFT OUTER JOIN department ON employee.DepartmentID == department.DepartmentID ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of "department" when no match for employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by employee.LastName, +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 872 +SELECT * FROM employee LEFT OUTER JOIN department ON employee.DepartmentID == department.DepartmentID ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of "department" when no match for employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by employee.LastName, +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 873 +SELECT * FROM employee RIGHT OUTER JOIN department ON employee.DepartmentID == department.DepartmentID ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of all but "department" when no match for employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by employee.LastName, +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 874 +SELECT * FROM employee RIGHT OUTER JOIN department ON employee.DepartmentID == department.DepartmentID ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of all but "department" when no match for employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by employee.LastName, +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 875 +SELECT * FROM employee FULL OUTER JOIN department ON employee.DepartmentID == none; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of "department" when no match for employee.DepartmentID == none +│ Extend the product with all NULL rows of all but "department" when no match for employee.DepartmentID == none +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 876 +SELECT * FROM employee FULL OUTER JOIN department ON employee.DepartmentID == none; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of "department" when no match for employee.DepartmentID == none +│ Extend the product with all NULL rows of all but "department" when no match for employee.DepartmentID == none +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 877 +SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.s1 == t2.s1; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "t1" +│ └Output field names ["s1"] +│ ┌Iterate all rows of table "t2" +│ └Output field names ["s1"] +│ Extend the product with all NULL rows of "t2" when no match for t1.s1 == t2.s1 +└Output field names ["t1.s1" "t2.s1"] + +---- 878 +SELECT * FROM a LEFT OUTER JOIN b ON a.i == b.i ORDER BY a.s, a.i, b.s, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "a" +│ └Output field names ["i" "s"] +│ ┌Iterate all rows of table "b" +│ └Output field names ["i" "s"] +│ Extend the product with all NULL rows of "b" when no match for a.i == b.i +└Output field names ["a.i" "a.s" "b.i" "b.s"] +┌Order by a.s, a.i, b.s, b.i, +└Output field names ["a.i" "a.s" "b.i" "b.s"] + +---- 879 +SELECT * FROM a RIGHT OUTER JOIN b ON a.i == b.i ORDER BY a.s, a.i, b.s, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "a" +│ └Output field names ["i" "s"] +│ ┌Iterate all rows of table "b" +│ └Output field names ["i" "s"] +│ Extend the product with all NULL rows of all but "b" when no match for a.i == b.i +└Output field names ["a.i" "a.s" "b.i" "b.s"] +┌Order by a.s, a.i, b.s, b.i, +└Output field names ["a.i" "a.s" "b.i" "b.s"] + +---- 880 +SELECT * FROM b LEFT OUTER JOIN a ON a.i == b.i ORDER BY a.s, a.i, b.s, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "b" +│ └Output field names ["i" "s"] +│ ┌Iterate all rows of table "a" +│ └Output field names ["i" "s"] +│ Extend the product with all NULL rows of "a" when no match for a.i == b.i +└Output field names ["b.i" "b.s" "a.i" "a.s"] +┌Order by a.s, a.i, b.s, b.i, +└Output field names ["b.i" "b.s" "a.i" "a.s"] + +---- 881 +SELECT * FROM b RIGHT OUTER JOIN a ON a.i == b.i ORDER BY a.s, a.i, b.s, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "b" +│ └Output field names ["i" "s"] +│ ┌Iterate all rows of table "a" +│ └Output field names ["i" "s"] +│ Extend the product with all NULL rows of all but "a" when no match for a.i == b.i +└Output field names ["b.i" "b.s" "a.i" "a.s"] +┌Order by a.s, a.i, b.s, b.i, +└Output field names ["b.i" "b.s" "a.i" "a.s"] + +---- 882 +SELECT * FROM a FULL OUTER JOIN b ON a.i == b.i ORDER BY a.s, a.i, b.s, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "a" +│ └Output field names ["i" "s"] +│ ┌Iterate all rows of table "b" +│ └Output field names ["i" "s"] +│ Extend the product with all NULL rows of "b" when no match for a.i == b.i +│ Extend the product with all NULL rows of all but "b" when no match for a.i == b.i +└Output field names ["a.i" "a.s" "b.i" "b.s"] +┌Order by a.s, a.i, b.s, b.i, +└Output field names ["a.i" "a.s" "b.i" "b.s"] + +---- 883 +SELECT * FROM a FULL OUTER JOIN b ON a.i == b.i ORDER BY a.s, a.i, b.s, b.i; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "a" +│ └Output field names ["i" "s"] +│ ┌Iterate all rows of table "b" +│ └Output field names ["i" "s"] +│ Extend the product with all NULL rows of "b" when no match for a.i == b.i +│ Extend the product with all NULL rows of all but "b" when no match for a.i == b.i +└Output field names ["a.i" "a.s" "b.i" "b.s"] +┌Order by a.s, a.i, b.s, b.i, +└Output field names ["a.i" "a.s" "b.i" "b.s"] + +---- 884 +SELECT * FROM employee FULL OUTER JOIN department ON employee.DepartmentID == department.DepartmentID ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of "department" when no match for employee.DepartmentID == department.DepartmentID +│ Extend the product with all NULL rows of all but "department" when no match for employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by employee.LastName, +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 885 +SELECT * FROM employee FULL OUTER JOIN department ON employee.DepartmentID == department.DepartmentID ORDER BY employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of "department" when no match for employee.DepartmentID == department.DepartmentID +│ Extend the product with all NULL rows of all but "department" when no match for employee.DepartmentID == department.DepartmentID +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by employee.LastName, +└Output field names ["employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 886 +SELECT * FROM t,employee LEFT OUTER JOIN department ON employee.DepartmentID == department.DepartmentID ORDER BY t.s, employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "t" +│ └Output field names ["s"] +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of "department" when no match for employee.DepartmentID == department.DepartmentID +└Output field names ["t.s" "employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by t.s, employee.LastName, +└Output field names ["t.s" "employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 887 +SELECT * FROM t,employee RIGHT OUTER JOIN department ON employee.DepartmentID == department.DepartmentID ORDER BY t.s, employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "t" +│ └Output field names ["s"] +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of all but "department" when no match for employee.DepartmentID == department.DepartmentID +└Output field names ["t.s" "employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by t.s, employee.LastName, +└Output field names ["t.s" "employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 888 +SELECT * FROM t,employee FULL OUTER JOIN department ON employee.DepartmentID == department.DepartmentID ORDER BY t.s, employee.LastName; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "t" +│ └Output field names ["s"] +│ ┌Iterate all rows of table "employee" +│ └Output field names ["LastName" "DepartmentID"] +│ ┌Iterate all rows of table "department" +│ └Output field names ["DepartmentID" "DepartmentName"] +│ Extend the product with all NULL rows of "department" when no match for employee.DepartmentID == department.DepartmentID +│ Extend the product with all NULL rows of all but "department" when no match for employee.DepartmentID == department.DepartmentID +└Output field names ["t.s" "employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] +┌Order by t.s, employee.LastName, +└Output field names ["t.s" "employee.LastName" "employee.DepartmentID" "department.DepartmentID" "department.DepartmentName"] + +---- 889 +SELECT * FROM __Table WHERE !hasPrefix(Name, "__") ORDER BY Name; +┌Iterate all rows of table "__Table" +└Output field names ["Name" "Schema"] +┌Filter on !hasPrefix(Name, "__") +└Output field names ["Name" "Schema"] +┌Order by Name, +└Output field names ["Name" "Schema"] + +---- 890 +SELECT * FROM __Column2 ORDER BY TableName, Name; +┌Iterate all rows of table "__Column2" +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] +┌Order by TableName, Name, +└Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] + +---- 891 +SELECT __Column.TableName, __Column.Ordinal, __Column.Name, __Column.Type, __Column2.NotNull, __Column2.ConstraintExpr, __Column2.DefaultExpr FROM __Column LEFT OUTER JOIN __Column2 ON __Column.TableName == __Column2.TableName && __Column.Name == __Column2.Name WHERE !hasPrefix(__Column.TableName, "__") ORDER BY __Column.TableName, __Column.Ordinal; +┌Compute Cartesian product of +│ ┌Iterate all rows of table "__Column" +│ └Output field names ["TableName" "Ordinal" "Name" "Type"] +│ ┌Iterate all rows of table "__Column2" +│ └Output field names ["TableName" "Name" "NotNull" "ConstraintExpr" "DefaultExpr"] +│ Extend the product with all NULL rows of "__Column2" when no match for __Column.TableName == __Column2.TableName && __Column.Name == __Column2.Name +└Output field names ["__Column.TableName" "__Column.Ordinal" "__Column.Name" "__Column.Type" "__Column2.TableName" "__Column2.Name" "__Column2.NotNull" "__Column2.ConstraintExpr" "__Column2.DefaultExpr"] +┌Filter on !hasPrefix(__Column.TableName, "__") +└Output field names ["__Column.TableName" "__Column.Ordinal" "__Column.Name" "__Column.Type" "__Column2.TableName" "__Column2.Name" "__Column2.NotNull" "__Column2.ConstraintExpr" "__Column2.DefaultExpr"] +┌Evaluate __Column.TableName as "__Column.TableName", __Column.Ordinal as "__Column.Ordinal", __Column.Name as "__Column.Name", __Column.Type as "__Column.Type", __Column2.NotNull as "__Column2.NotNull", __Column2.ConstraintExpr as "__Column2.ConstraintExpr", __Column2.DefaultExpr as "__Column2.DefaultExpr", +└Output field names ["__Column.TableName" "__Column.Ordinal" "__Column.Name" "__Column.Type" "__Column2.NotNull" "__Column2.ConstraintExpr" "__Column2.DefaultExpr"] +┌Order by __Column.TableName, __Column.Ordinal, +└Output field names ["__Column.TableName" "__Column.Ordinal" "__Column.Name" "__Column.Type" "__Column2.NotNull" "__Column2.ConstraintExpr" "__Column2.DefaultExpr"] + +---- 902 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__"); +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 903 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x";); +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] + +---- 904 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__"); +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 905 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x";); +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] + +---- 906 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 907 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 908 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 909 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 910 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 911 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 912 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 913 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 914 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 915 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 916 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 917 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 918 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 919 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 920 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 921 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 922 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 923 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 924 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 925 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 926 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 927 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 928 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 929 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 930 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE !hasPrefix(TableName, "__") ORDER BY IndexName; +┌Iterate all rows of table "__Index2" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Filter on !hasPrefix(TableName, "__") +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] +┌Order by IndexName, +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 931 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 932 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 933 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 934 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 935 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 936 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 937 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x" || IndexName == "y";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 938 +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 || Root == -1 FROM __Index2 WHERE TableName == "t"; +┌Iterate all rows of table "__Index2" using index "__xIndex2_TableName" where TableName == "t" +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" "Root"] +┌Evaluate TableName as "TableName", IndexName as "IndexName", IsUnique as "IsUnique", IsSimple as "IsSimple", Root > 0 || Root == -1 as "", +└Output field names ["TableName" "IndexName" "IsUnique" "IsSimple" ""] + +---- 939 +SELECT Expr FROM __Index2_Expr WHERE Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x";) ORDER BY Expr; +┌Iterate all rows of table "__Index2_Expr" +└Output field names ["Index2_ID" "Expr"] +┌Filter on Index2_ID IN (SELECT id() FROM __Index2 WHERE IndexName == "x";) +└Output field names ["Index2_ID" "Expr"] +┌Evaluate Expr as "Expr", +└Output field names ["Expr"] +┌Order by Expr, +└Output field names ["Expr"] + +---- 940 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 941 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 942 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["a" "b" "c"] + +---- 943 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 950 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 952 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 953 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 954 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 955 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 956 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 957 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 959 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 963 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 964 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["c"] + +---- 966 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 967 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 968 +SELECT * FROM y; +┌Iterate all values of index "y" +└Output field names N/A + +---- 970 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 971 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 972 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 974 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 975 +SELECT * FROM x; +┌Iterate all values of index "x" +└Output field names N/A + +---- 981 +EXPLAIN SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 982 +EXPLAIN EXPLAIN SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 983 +SELECT * FROM t WHERE i != 42; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i != 42 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i"] + +---- 984 +SELECT * FROM t WHERE i != 42; +┌Iterate all rows of table "t" using index "x" where i != 42 +└Output field names ["i"] + +---- 985 +SELECT * FROM t WHERE i != 42; +┌Iterate all rows of table "t" using index "x" where i != 42 +└Output field names ["i"] + +---- 986 +SELECT * FROM t WHERE id() > 0; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 987 +SELECT * FROM t WHERE id() > 0; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 988 +SELECT * FROM t WHERE i IS NULL; +┌Iterate all rows of table "t" +└Output field names ["i" "j"] +┌Filter on i IS NULL +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i" "j"] + +---- 989 +SELECT * FROM t WHERE i IS NULL; +┌Iterate all rows of table "t" using index "x" where i IS NULL +└Output field names ["i" "j"] + +---- 990 +SELECT * FROM t WHERE i IS NOT NULL; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i IS NOT NULL +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i"] + +---- 991 +SELECT * FROM t WHERE i IS NOT NULL; +┌Iterate all rows of table "t" using index "x" where i IS NOT NULL +└Output field names ["i"] + +---- 992 +SELECT * FROM t WHERE id() IS NULL; +┌Iterate no rows +└Output field names ["i"] + +---- 993 +SELECT * FROM t WHERE id() IS NOT NULL; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 994 +SELECT * FROM t WHERE id() IS NULL; +┌Iterate no rows +└Output field names ["i"] + +---- 995 +SELECT * FROM t WHERE id() IS NOT NULL; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 996 +SELECT * FROM t WHERE id() == 0; +┌Iterate no rows +└Output field names ["i"] + +---- 997 +SELECT * FROM t WHERE id() == 0; +┌Iterate no rows +└Output field names ["i"] + +---- 998 +SELECT * FROM t WHERE id() < 1; +┌Iterate no rows +└Output field names ["i"] + +---- 999 +SELECT * FROM t WHERE id() < 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1000 +SELECT * FROM t WHERE id() <= 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1001 +SELECT * FROM t WHERE id() <= 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1002 +SELECT * FROM t WHERE id() > 0; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 1003 +SELECT * FROM t WHERE id() > 0; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 1004 +SELECT * FROM t WHERE id() >= 1; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 1005 +SELECT * FROM t WHERE id() >= 1; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 1006 +SELECT * FROM t WHERE id() != 0; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 1007 +SELECT * FROM t WHERE id() != 0; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 1008 +SELECT * FROM t WHERE i > -1 && i < 314 || i > 1000 && i < 2000; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i > -1 && i < 314 || i > 1000 && i < 2000 +└Output field names ["i"] + +---- 1009 +SELECT i FROM t WHERE !b ORDER BY i; +┌Iterate all rows of table "t" using index "x" where !b +└Output field names ["i" "b"] +┌Evaluate i as "i", +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 1010 +SELECT i FROM t WHERE i == 0 && i == -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1011 +SELECT i FROM t WHERE i == 0 && i == -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1012 +SELECT i FROM t WHERE i == 0 && i == 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1013 +SELECT i FROM t WHERE i == 0 && i == 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1014 +SELECT i FROM t WHERE i == 0 && i == 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1015 +SELECT i FROM t WHERE i == 0 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1016 +SELECT i FROM t WHERE i == 0 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1017 +SELECT i FROM t WHERE i == 0 && i >= 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1018 +SELECT i FROM t WHERE i == 0 && i >= 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1019 +SELECT i FROM t WHERE i == 0 && i >= 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1020 +SELECT i FROM t WHERE i == 0 && i > -2; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1021 +SELECT i FROM t WHERE i == 0 && i > -1; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1022 +SELECT i FROM t WHERE i == 0 && i > 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1023 +SELECT i FROM t WHERE i == 0 && i > 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1024 +SELECT i FROM t WHERE i == 0 && i > 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1025 +SELECT i FROM t WHERE i == 0 && i <= -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1026 +SELECT i FROM t WHERE i == 0 && i <= -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1027 +SELECT i FROM t WHERE i == 0 && i <= 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1028 +SELECT i FROM t WHERE i == 0 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1029 +SELECT i FROM t WHERE i == 0 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1030 +SELECT i FROM t WHERE i == 0 && i < -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1031 +SELECT i FROM t WHERE i == 0 && i < -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1032 +SELECT i FROM t WHERE i == 0 && i < 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1033 +SELECT i FROM t WHERE i == 0 && i < 1; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1034 +SELECT i FROM t WHERE i == 0 && i < 2; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1035 +SELECT i FROM t WHERE i == 0 && i != -2; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1036 +SELECT i FROM t WHERE i == 0 && i != -1; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1037 +SELECT i FROM t WHERE i == 0 && i != 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1038 +SELECT i FROM t WHERE i == 0 && i != 1; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1039 +SELECT i FROM t WHERE i == 0 && i != 2; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1040 +SELECT * FROM t WHERE !b && b ORDER BY i; +┌Iterate no rows +└Output field names ["i" "b"] + +---- 1041 +SELECT * FROM t WHERE !b && !b ORDER BY i; +┌Iterate all rows of table "t" using index "x" where !b +└Output field names ["i" "b"] +┌Order by i, +└Output field names ["i" "b"] + +---- 1042 +SELECT * FROM t WHERE b && !b ORDER BY i; +┌Iterate no rows +└Output field names ["i" "b"] + +---- 1043 +SELECT * FROM t WHERE b && b ORDER BY i; +┌Iterate all rows of table "t" using index "x" where b +└Output field names ["i" "b"] +┌Order by i, +└Output field names ["i" "b"] + +---- 1045 +SELECT i FROM t WHERE i >= 0 && i == -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1046 +SELECT i FROM t WHERE i >= 0 && i == -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1047 +SELECT i FROM t WHERE i >= 0 && i == 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1048 +SELECT i FROM t WHERE i >= 0 && i == 1; +┌Iterate all rows of table "t" using index "x" where i == 1 +└Output field names ["i"] + +---- 1049 +SELECT i FROM t WHERE i >= 0 && i == 2; +┌Iterate all rows of table "t" using index "x" where i == 2 +└Output field names ["i"] + +---- 1050 +SELECT i FROM t WHERE i >= 0 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i >= 0 +└Output field names ["i"] + +---- 1051 +SELECT i FROM t WHERE i >= 0 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i >= 0 +└Output field names ["i"] + +---- 1052 +SELECT i FROM t WHERE i >= 0 && i >= 0; +┌Iterate all rows of table "t" using index "x" where i >= 0 +└Output field names ["i"] + +---- 1053 +SELECT i FROM t WHERE i >= 0 && i >= 1; +┌Iterate all rows of table "t" using index "x" where i >= 1 +└Output field names ["i"] + +---- 1054 +SELECT i FROM t WHERE i >= 0 && i >= 2; +┌Iterate all rows of table "t" using index "x" where i >= 2 +└Output field names ["i"] + +---- 1055 +SELECT i FROM t WHERE i >= 0 && i > -2; +┌Iterate all rows of table "t" using index "x" where i >= 0 +└Output field names ["i"] + +---- 1056 +SELECT i FROM t WHERE i >= 0 && i > -1; +┌Iterate all rows of table "t" using index "x" where i >= 0 +└Output field names ["i"] + +---- 1057 +SELECT i FROM t WHERE i >= 0 && i > 0; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1058 +SELECT i FROM t WHERE i >= 0 && i > 1; +┌Iterate all rows of table "t" using index "x" where i > 1 +└Output field names ["i"] + +---- 1059 +SELECT i FROM t WHERE i >= 0 && i > 2; +┌Iterate all rows of table "t" using index "x" where i > 2 +└Output field names ["i"] + +---- 1060 +SELECT i FROM t WHERE i >= 0 && i <= -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1061 +SELECT i FROM t WHERE i >= 0 && i <= -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1062 +SELECT i FROM t WHERE i >= 0 && i <= 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1063 +SELECT i FROM t WHERE i >= 0 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i >= 0 && i <= 1 +└Output field names ["i"] + +---- 1064 +SELECT i FROM t WHERE i >= 0 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i >= 0 && i <= 2 +└Output field names ["i"] + +---- 1065 +SELECT i FROM t WHERE i >= 0 && i < -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1066 +SELECT i FROM t WHERE i >= 0 && i < -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1067 +SELECT i FROM t WHERE i >= 0 && i < 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1068 +SELECT i FROM t WHERE i >= 0 && i < 1; +┌Iterate all rows of table "t" using index "x" where i >= 0 && i < 1 +└Output field names ["i"] + +---- 1069 +SELECT i FROM t WHERE i >= 0 && i < 2; +┌Iterate all rows of table "t" using index "x" where i >= 0 && i < 2 +└Output field names ["i"] + +---- 1070 +SELECT i FROM t WHERE i >= 0 && i != -2; +┌Iterate all rows of table "t" using index "x" where i >= 0 +└Output field names ["i"] + +---- 1071 +SELECT i FROM t WHERE i >= 0 && i != -1; +┌Iterate all rows of table "t" using index "x" where i >= 0 +└Output field names ["i"] + +---- 1072 +SELECT i FROM t WHERE i >= 0 && i != 0; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1073 +SELECT i FROM t WHERE i >= 0 && i != 1; +┌Iterate all rows of table "t" using index "x" where i >= 0 +└Output field names ["i"] +┌Filter on i != 1 +└Output field names ["i"] + +---- 1074 +SELECT i FROM t WHERE i >= 0 && i != 2; +┌Iterate all rows of table "t" using index "x" where i >= 0 +└Output field names ["i"] +┌Filter on i != 2 +└Output field names ["i"] + +---- 1075 +SELECT i FROM t WHERE i > 0 && i == -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1076 +SELECT i FROM t WHERE i > 0 && i == -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1077 +SELECT i FROM t WHERE i > 0 && i == 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1078 +SELECT i FROM t WHERE i > 0 && i == 1; +┌Iterate all rows of table "t" using index "x" where i == 1 +└Output field names ["i"] + +---- 1079 +SELECT i FROM t WHERE i > 0 && i == 2; +┌Iterate all rows of table "t" using index "x" where i == 2 +└Output field names ["i"] + +---- 1080 +SELECT i FROM t WHERE i > 0 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1081 +SELECT i FROM t WHERE i > 0 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1082 +SELECT i FROM t WHERE i > 0 && i >= 0; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1083 +SELECT i FROM t WHERE i > 0 && i >= 1; +┌Iterate all rows of table "t" using index "x" where i >= 1 +└Output field names ["i"] + +---- 1084 +SELECT i FROM t WHERE i > 0 && i >= 2; +┌Iterate all rows of table "t" using index "x" where i >= 2 +└Output field names ["i"] + +---- 1085 +SELECT i FROM t WHERE i > 0 && i > -2; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1086 +SELECT i FROM t WHERE i > 0 && i > -1; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1087 +SELECT i FROM t WHERE i > 0 && i > 0; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1088 +SELECT i FROM t WHERE i > 0 && i > 1; +┌Iterate all rows of table "t" using index "x" where i > 1 +└Output field names ["i"] + +---- 1089 +SELECT i FROM t WHERE i > 0 && i > 2; +┌Iterate all rows of table "t" using index "x" where i > 2 +└Output field names ["i"] + +---- 1090 +SELECT i FROM t WHERE i > 0 && i <= -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1091 +SELECT i FROM t WHERE i > 0 && i <= -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1092 +SELECT i FROM t WHERE i > 0 && i <= 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1093 +SELECT i FROM t WHERE i > 0 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i > 0 && i <= 1 +└Output field names ["i"] + +---- 1094 +SELECT i FROM t WHERE i > 0 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i > 0 && i <= 2 +└Output field names ["i"] + +---- 1095 +SELECT i FROM t WHERE i > 0 && i < -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1096 +SELECT i FROM t WHERE i > 0 && i < -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1097 +SELECT i FROM t WHERE i > 0 && i < 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1098 +SELECT i FROM t WHERE i > 0 && i < 1; +┌Iterate all rows of table "t" using index "x" where i > 0 && i < 1 +└Output field names ["i"] + +---- 1099 +SELECT i FROM t WHERE i > 0 && i < 2; +┌Iterate all rows of table "t" using index "x" where i > 0 && i < 2 +└Output field names ["i"] + +---- 1100 +SELECT i FROM t WHERE i > 0 && i != -2; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1101 +SELECT i FROM t WHERE i > 0 && i != -1; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1102 +SELECT i FROM t WHERE i > 0 && i != 0; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1103 +SELECT i FROM t WHERE i > 0 && i != 1; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] +┌Filter on i != 1 +└Output field names ["i"] + +---- 1104 +SELECT i FROM t WHERE i > 0 && i != 2; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] +┌Filter on i != 2 +└Output field names ["i"] + +---- 1105 +SELECT i FROM t WHERE i <= 0 && i == -2; +┌Iterate all rows of table "t" using index "x" where i == -2 +└Output field names ["i"] + +---- 1106 +SELECT i FROM t WHERE i <= 0 && i == -1; +┌Iterate all rows of table "t" using index "x" where i == -1 +└Output field names ["i"] + +---- 1107 +SELECT i FROM t WHERE i <= 0 && i == 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1108 +SELECT i FROM t WHERE i <= 0 && i == 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1109 +SELECT i FROM t WHERE i <= 0 && i == 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1110 +SELECT i FROM t WHERE i <= 0 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i >= -2 && i <= 0 +└Output field names ["i"] + +---- 1111 +SELECT i FROM t WHERE i <= 0 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 0 +└Output field names ["i"] + +---- 1112 +SELECT i FROM t WHERE i <= 0 && i >= 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1113 +SELECT i FROM t WHERE i <= 0 && i >= 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1114 +SELECT i FROM t WHERE i <= 0 && i >= 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1115 +SELECT i FROM t WHERE i <= 0 && i > -2; +┌Iterate all rows of table "t" using index "x" where i > -2 && i <= 0 +└Output field names ["i"] + +---- 1116 +SELECT i FROM t WHERE i <= 0 && i > -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 0 +└Output field names ["i"] + +---- 1117 +SELECT i FROM t WHERE i <= 0 && i > 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1118 +SELECT i FROM t WHERE i <= 0 && i > 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1119 +SELECT i FROM t WHERE i <= 0 && i > 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1120 +SELECT i FROM t WHERE i <= 0 && i <= -2; +┌Iterate all rows of table "t" using index "x" where i <= -2 +└Output field names ["i"] + +---- 1121 +SELECT i FROM t WHERE i <= 0 && i <= -1; +┌Iterate all rows of table "t" using index "x" where i <= -1 +└Output field names ["i"] + +---- 1122 +SELECT i FROM t WHERE i <= 0 && i <= 0; +┌Iterate all rows of table "t" using index "x" where i <= 0 +└Output field names ["i"] + +---- 1123 +SELECT i FROM t WHERE i <= 0 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i <= 0 +└Output field names ["i"] + +---- 1124 +SELECT i FROM t WHERE i <= 0 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i <= 0 +└Output field names ["i"] + +---- 1125 +SELECT i FROM t WHERE i <= 0 && i < -2; +┌Iterate all rows of table "t" using index "x" where i < -2 +└Output field names ["i"] + +---- 1126 +SELECT i FROM t WHERE i <= 0 && i < -1; +┌Iterate all rows of table "t" using index "x" where i < -1 +└Output field names ["i"] + +---- 1127 +SELECT i FROM t WHERE i <= 0 && i < 0; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1128 +SELECT i FROM t WHERE i <= 0 && i < 1; +┌Iterate all rows of table "t" using index "x" where i <= 0 +└Output field names ["i"] + +---- 1129 +SELECT i FROM t WHERE i <= 0 && i < 2; +┌Iterate all rows of table "t" using index "x" where i <= 0 +└Output field names ["i"] + +---- 1130 +SELECT i FROM t WHERE i <= 0 && i != -2; +┌Iterate all rows of table "t" using index "x" where i <= 0 +└Output field names ["i"] +┌Filter on i != -2 +└Output field names ["i"] + +---- 1131 +SELECT i FROM t WHERE i <= 0 && i != -1; +┌Iterate all rows of table "t" using index "x" where i <= 0 +└Output field names ["i"] +┌Filter on i != -1 +└Output field names ["i"] + +---- 1132 +SELECT i FROM t WHERE i <= 0 && i != 0; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1133 +SELECT i FROM t WHERE i <= 0 && i != 1; +┌Iterate all rows of table "t" using index "x" where i <= 0 +└Output field names ["i"] + +---- 1134 +SELECT i FROM t WHERE i <= 0 && i != 2; +┌Iterate all rows of table "t" using index "x" where i <= 0 +└Output field names ["i"] + +---- 1135 +SELECT i FROM t WHERE i < 0 && i == -2; +┌Iterate all rows of table "t" using index "x" where i == -2 +└Output field names ["i"] + +---- 1136 +SELECT i FROM t WHERE i < 0 && i == -1; +┌Iterate all rows of table "t" using index "x" where i == -1 +└Output field names ["i"] + +---- 1137 +SELECT i FROM t WHERE i < 0 && i == 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1138 +SELECT i FROM t WHERE i < 0 && i == 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1139 +SELECT i FROM t WHERE i < 0 && i == 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1140 +SELECT i FROM t WHERE i < 0 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i >= -2 && i < 0 +└Output field names ["i"] + +---- 1141 +SELECT i FROM t WHERE i < 0 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 0 +└Output field names ["i"] + +---- 1142 +SELECT i FROM t WHERE i < 0 && i >= 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1143 +SELECT i FROM t WHERE i < 0 && i >= 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1144 +SELECT i FROM t WHERE i < 0 && i >= 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1145 +SELECT i FROM t WHERE i < 0 && i > -2; +┌Iterate all rows of table "t" using index "x" where i > -2 && i < 0 +└Output field names ["i"] + +---- 1146 +SELECT i FROM t WHERE i < 0 && i > -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 0 +└Output field names ["i"] + +---- 1147 +SELECT i FROM t WHERE i < 0 && i > 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1148 +SELECT i FROM t WHERE i < 0 && i > 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1149 +SELECT i FROM t WHERE i < 0 && i > 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1150 +SELECT i FROM t WHERE i < 0 && i <= -2; +┌Iterate all rows of table "t" using index "x" where i <= -2 +└Output field names ["i"] + +---- 1151 +SELECT i FROM t WHERE i < 0 && i <= -1; +┌Iterate all rows of table "t" using index "x" where i <= -1 +└Output field names ["i"] + +---- 1152 +SELECT i FROM t WHERE i < 0 && i <= 0; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1153 +SELECT i FROM t WHERE i < 0 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1154 +SELECT i FROM t WHERE i < 0 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1155 +SELECT i FROM t WHERE i < 0 && i < -2; +┌Iterate all rows of table "t" using index "x" where i < -2 +└Output field names ["i"] + +---- 1156 +SELECT i FROM t WHERE i < 0 && i < -1; +┌Iterate all rows of table "t" using index "x" where i < -1 +└Output field names ["i"] + +---- 1157 +SELECT i FROM t WHERE i < 0 && i < 0; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1158 +SELECT i FROM t WHERE i < 0 && i < 1; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1159 +SELECT i FROM t WHERE i < 0 && i < 2; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1160 +SELECT i FROM t WHERE i < 0 && i != -2; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] +┌Filter on i != -2 +└Output field names ["i"] + +---- 1161 +SELECT i FROM t WHERE i < 0 && i != -1; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] +┌Filter on i != -1 +└Output field names ["i"] + +---- 1162 +SELECT i FROM t WHERE i < 0 && i != 0; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1163 +SELECT i FROM t WHERE i < 0 && i != 1; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1164 +SELECT i FROM t WHERE i < 0 && i != 2; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1165 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1166 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == -1; +┌Iterate all rows of table "t" using index "x" where i == -1 +└Output field names ["i"] + +---- 1167 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1168 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == 1; +┌Iterate all rows of table "t" using index "x" where i == 1 +└Output field names ["i"] + +---- 1169 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1170 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 1 +└Output field names ["i"] + +---- 1171 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 1 +└Output field names ["i"] + +---- 1172 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= 0; +┌Iterate all rows of table "t" using index "x" where i >= 0 && i <= 1 +└Output field names ["i"] + +---- 1173 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= 1; +┌Iterate all rows of table "t" using index "x" where i == 1 +└Output field names ["i"] + +---- 1174 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1175 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > -2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 1 +└Output field names ["i"] + +---- 1176 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1177 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > 0; +┌Iterate all rows of table "t" using index "x" where i > 0 && i <= 1 +└Output field names ["i"] + +---- 1178 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1179 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1180 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1181 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= -1; +┌Iterate all rows of table "t" using index "x" where i == -1 +└Output field names ["i"] + +---- 1182 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= 0; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 0 +└Output field names ["i"] + +---- 1183 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 1 +└Output field names ["i"] + +---- 1184 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 1 +└Output field names ["i"] + +---- 1185 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1186 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1187 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < 0; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 0 +└Output field names ["i"] + +---- 1188 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < 1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1189 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < 2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 1 +└Output field names ["i"] + +---- 1190 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != -2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 1 +└Output field names ["i"] + +---- 1191 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1192 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != 0; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 1 +└Output field names ["i"] +┌Filter on i != 0 +└Output field names ["i"] + +---- 1193 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != 1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1194 +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != 2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 1 +└Output field names ["i"] + +---- 1195 +SELECT i FROM t WHERE i > -1 && i <= 1 && i == -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1196 +SELECT i FROM t WHERE i > -1 && i <= 1 && i == -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1197 +SELECT i FROM t WHERE i > -1 && i <= 1 && i == 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1198 +SELECT i FROM t WHERE i > -1 && i <= 1 && i == 1; +┌Iterate all rows of table "t" using index "x" where i == 1 +└Output field names ["i"] + +---- 1199 +SELECT i FROM t WHERE i > -1 && i <= 1 && i == 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1200 +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1201 +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1202 +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= 0; +┌Iterate all rows of table "t" using index "x" where i >= 0 && i <= 1 +└Output field names ["i"] + +---- 1203 +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= 1; +┌Iterate all rows of table "t" using index "x" where i == 1 +└Output field names ["i"] + +---- 1204 +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1205 +SELECT i FROM t WHERE i > -1 && i <= 1 && i > -2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1206 +SELECT i FROM t WHERE i > -1 && i <= 1 && i > -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1207 +SELECT i FROM t WHERE i > -1 && i <= 1 && i > 0; +┌Iterate all rows of table "t" using index "x" where i > 0 && i <= 1 +└Output field names ["i"] + +---- 1208 +SELECT i FROM t WHERE i > -1 && i <= 1 && i > 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1209 +SELECT i FROM t WHERE i > -1 && i <= 1 && i > 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1210 +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1211 +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1212 +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= 0; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 0 +└Output field names ["i"] + +---- 1213 +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1214 +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1215 +SELECT i FROM t WHERE i > -1 && i <= 1 && i < -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1216 +SELECT i FROM t WHERE i > -1 && i <= 1 && i < -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1217 +SELECT i FROM t WHERE i > -1 && i <= 1 && i < 0; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 0 +└Output field names ["i"] + +---- 1218 +SELECT i FROM t WHERE i > -1 && i <= 1 && i < 1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1219 +SELECT i FROM t WHERE i > -1 && i <= 1 && i < 2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1220 +SELECT i FROM t WHERE i > -1 && i <= 1 && i != -2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1221 +SELECT i FROM t WHERE i > -1 && i <= 1 && i != -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1222 +SELECT i FROM t WHERE i > -1 && i <= 1 && i != 0; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] +┌Filter on i != 0 +└Output field names ["i"] + +---- 1223 +SELECT i FROM t WHERE i > -1 && i <= 1 && i != 1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1224 +SELECT i FROM t WHERE i > -1 && i <= 1 && i != 2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 1 +└Output field names ["i"] + +---- 1225 +SELECT i FROM t WHERE i > -1 && i < 1 && i == -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1226 +SELECT i FROM t WHERE i > -1 && i < 1 && i == -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1227 +SELECT i FROM t WHERE i > -1 && i < 1 && i == 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1228 +SELECT i FROM t WHERE i > -1 && i < 1 && i == 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1229 +SELECT i FROM t WHERE i > -1 && i < 1 && i == 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1230 +SELECT i FROM t WHERE i > -1 && i < 1 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1231 +SELECT i FROM t WHERE i > -1 && i < 1 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1232 +SELECT i FROM t WHERE i > -1 && i < 1 && i >= 0; +┌Iterate all rows of table "t" using index "x" where i >= 0 && i < 1 +└Output field names ["i"] + +---- 1233 +SELECT i FROM t WHERE i > -1 && i < 1 && i >= 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1234 +SELECT i FROM t WHERE i > -1 && i < 1 && i >= 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1235 +SELECT i FROM t WHERE i > -1 && i < 1 && i > -2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1236 +SELECT i FROM t WHERE i > -1 && i < 1 && i > -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1237 +SELECT i FROM t WHERE i > -1 && i < 1 && i > 0; +┌Iterate all rows of table "t" using index "x" where i > 0 && i < 1 +└Output field names ["i"] + +---- 1238 +SELECT i FROM t WHERE i > -1 && i < 1 && i > 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1239 +SELECT i FROM t WHERE i > -1 && i < 1 && i > 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1240 +SELECT i FROM t WHERE i > -1 && i < 1 && i <= -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1241 +SELECT i FROM t WHERE i > -1 && i < 1 && i <= -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1242 +SELECT i FROM t WHERE i > -1 && i < 1 && i <= 0; +┌Iterate all rows of table "t" using index "x" where i > -1 && i <= 0 +└Output field names ["i"] + +---- 1243 +SELECT i FROM t WHERE i > -1 && i < 1 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1244 +SELECT i FROM t WHERE i > -1 && i < 1 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1245 +SELECT i FROM t WHERE i > -1 && i < 1 && i < -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1246 +SELECT i FROM t WHERE i > -1 && i < 1 && i < -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1247 +SELECT i FROM t WHERE i > -1 && i < 1 && i < 0; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 0 +└Output field names ["i"] + +---- 1248 +SELECT i FROM t WHERE i > -1 && i < 1 && i < 1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1249 +SELECT i FROM t WHERE i > -1 && i < 1 && i < 2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1250 +SELECT i FROM t WHERE i > -1 && i < 1 && i != -2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1251 +SELECT i FROM t WHERE i > -1 && i < 1 && i != -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1252 +SELECT i FROM t WHERE i > -1 && i < 1 && i != 0; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] +┌Filter on i != 0 +└Output field names ["i"] + +---- 1253 +SELECT i FROM t WHERE i > -1 && i < 1 && i != 1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1254 +SELECT i FROM t WHERE i > -1 && i < 1 && i != 2; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1255 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1256 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == -1; +┌Iterate all rows of table "t" using index "x" where i == -1 +└Output field names ["i"] + +---- 1257 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1258 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1259 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1260 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1261 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == -1; +┌Iterate all rows of table "t" using index "x" where i == -1 +└Output field names ["i"] + +---- 1262 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 0; +┌Iterate all rows of table "t" using index "x" where i == 0 +└Output field names ["i"] + +---- 1263 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1264 +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1265 +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1266 +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1267 +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= 0; +┌Iterate all rows of table "t" using index "x" where i >= 0 && i < 1 +└Output field names ["i"] + +---- 1268 +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1269 +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1270 +SELECT i FROM t WHERE i >= -1 && i < 1 && i > -2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1271 +SELECT i FROM t WHERE i >= -1 && i < 1 && i > -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1272 +SELECT i FROM t WHERE i >= -1 && i < 1 && i > 0; +┌Iterate all rows of table "t" using index "x" where i > 0 && i < 1 +└Output field names ["i"] + +---- 1273 +SELECT i FROM t WHERE i >= -1 && i < 1 && i > 1; +┌Iterate no rows +└Output field names ["i"] + +---- 1274 +SELECT i FROM t WHERE i >= -1 && i < 1 && i > 2; +┌Iterate no rows +└Output field names ["i"] + +---- 1275 +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1276 +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= -1; +┌Iterate all rows of table "t" using index "x" where i == -1 +└Output field names ["i"] + +---- 1277 +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= 0; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i <= 0 +└Output field names ["i"] + +---- 1278 +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1279 +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1280 +SELECT i FROM t WHERE i >= -1 && i < 1 && i < -2; +┌Iterate no rows +└Output field names ["i"] + +---- 1281 +SELECT i FROM t WHERE i >= -1 && i < 1 && i < -1; +┌Iterate no rows +└Output field names ["i"] + +---- 1282 +SELECT i FROM t WHERE i >= -1 && i < 1 && i < 0; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 0 +└Output field names ["i"] + +---- 1283 +SELECT i FROM t WHERE i >= -1 && i < 1 && i < 1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1284 +SELECT i FROM t WHERE i >= -1 && i < 1 && i < 2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1285 +SELECT i FROM t WHERE i >= -1 && i < 1 && i != -2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1286 +SELECT i FROM t WHERE i >= -1 && i < 1 && i != -1; +┌Iterate all rows of table "t" using index "x" where i > -1 && i < 1 +└Output field names ["i"] + +---- 1287 +SELECT i FROM t WHERE i >= -1 && i < 1 && i != 0; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] +┌Filter on i != 0 +└Output field names ["i"] + +---- 1288 +SELECT i FROM t WHERE i >= -1 && i < 1 && i != 1; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1289 +SELECT i FROM t WHERE i >= -1 && i < 1 && i != 2; +┌Iterate all rows of table "t" using index "x" where i >= -1 && i < 1 +└Output field names ["i"] + +---- 1290 +SELECT i FROM t WHERE i != 0 && i == -2; +┌Iterate all rows of table "t" using index "x" where i == -2 +└Output field names ["i"] + +---- 1291 +SELECT i FROM t WHERE i != 0 && i == -1; +┌Iterate all rows of table "t" using index "x" where i == -1 +└Output field names ["i"] + +---- 1292 +SELECT i FROM t WHERE i != 0 && i == 0; +┌Iterate no rows +└Output field names ["i"] + +---- 1293 +SELECT i FROM t WHERE i != 0 && i == 1; +┌Iterate all rows of table "t" using index "x" where i == 1 +└Output field names ["i"] + +---- 1294 +SELECT i FROM t WHERE i != 0 && i == 2; +┌Iterate all rows of table "t" using index "x" where i == 2 +└Output field names ["i"] + +---- 1295 +SELECT i FROM t WHERE i != 0 && i >= -2; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i >= -2 +└Output field names ["i"] + +---- 1296 +SELECT i FROM t WHERE i != 0 && i >= -1; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i >= -1 +└Output field names ["i"] + +---- 1297 +SELECT i FROM t WHERE i != 0 && i >= 0; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1298 +SELECT i FROM t WHERE i != 0 && i >= 1; +┌Iterate all rows of table "t" using index "x" where i >= 1 +└Output field names ["i"] + +---- 1299 +SELECT i FROM t WHERE i != 0 && i >= 2; +┌Iterate all rows of table "t" using index "x" where i >= 2 +└Output field names ["i"] + +---- 1300 +SELECT i FROM t WHERE i != 0 && i > -2; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i > -2 +└Output field names ["i"] + +---- 1301 +SELECT i FROM t WHERE i != 0 && i > -1; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i > -1 +└Output field names ["i"] + +---- 1302 +SELECT i FROM t WHERE i != 0 && i > 0; +┌Iterate all rows of table "t" using index "x" where i > 0 +└Output field names ["i"] + +---- 1303 +SELECT i FROM t WHERE i != 0 && i > 1; +┌Iterate all rows of table "t" using index "x" where i > 1 +└Output field names ["i"] + +---- 1304 +SELECT i FROM t WHERE i != 0 && i > 2; +┌Iterate all rows of table "t" using index "x" where i > 2 +└Output field names ["i"] + +---- 1305 +SELECT i FROM t WHERE i != 0 && i <= -2; +┌Iterate all rows of table "t" using index "x" where i <= -2 +└Output field names ["i"] + +---- 1306 +SELECT i FROM t WHERE i != 0 && i <= -1; +┌Iterate all rows of table "t" using index "x" where i <= -1 +└Output field names ["i"] + +---- 1307 +SELECT i FROM t WHERE i != 0 && i <= 0; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1308 +SELECT i FROM t WHERE i != 0 && i <= 1; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i <= 1 +└Output field names ["i"] + +---- 1309 +SELECT i FROM t WHERE i != 0 && i <= 2; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i <= 2 +└Output field names ["i"] + +---- 1310 +SELECT i FROM t WHERE i != 0 && i < -2; +┌Iterate all rows of table "t" using index "x" where i < -2 +└Output field names ["i"] + +---- 1311 +SELECT i FROM t WHERE i != 0 && i < -1; +┌Iterate all rows of table "t" using index "x" where i < -1 +└Output field names ["i"] + +---- 1312 +SELECT i FROM t WHERE i != 0 && i < 0; +┌Iterate all rows of table "t" using index "x" where i < 0 +└Output field names ["i"] + +---- 1313 +SELECT i FROM t WHERE i != 0 && i < 1; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i < 1 +└Output field names ["i"] + +---- 1314 +SELECT i FROM t WHERE i != 0 && i < 2; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i < 2 +└Output field names ["i"] + +---- 1315 +SELECT i FROM t WHERE i != 0 && i != -2; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i != -2 +└Output field names ["i"] + +---- 1316 +SELECT i FROM t WHERE i != 0 && i != -1; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i != -1 +└Output field names ["i"] + +---- 1317 +SELECT i FROM t WHERE i != 0 && i != 0; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] + +---- 1318 +SELECT i FROM t WHERE i != 0 && i != 1; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i != 1 +└Output field names ["i"] + +---- 1319 +SELECT i FROM t WHERE i != 0 && i != 2; +┌Iterate all rows of table "t" using index "x" where i != 0 +└Output field names ["i"] +┌Filter on i != 2 +└Output field names ["i"] + +---- 1320 +SELECT * FROM t WHERE i > 0 && j > 0 ORDER BY i, j; +┌Iterate all rows of table "t" +└Output field names ["i" "j" "k"] +┌Filter on i > 0 && j > 0 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +│CREATE INDEX xt_j ON t(j); +└Output field names ["i" "j" "k"] +┌Order by i, j, +└Output field names ["i" "j" "k"] + +---- 1321 +SELECT * FROM t WHERE i > 0 && j > 0 ORDER BY i, j; +┌Iterate all rows of table "t" using index "xi" where i > 0 +└Output field names ["i" "j" "k"] +┌Filter on j > 0 +└Output field names ["i" "j" "k"] +┌Order by i, j, +└Output field names ["i" "j" "k"] + +---- 1322 +SELECT * FROM t WHERE i > 0 && j > 0 ORDER BY i, j; +┌Iterate all rows of table "t" using index "xj" where j > 0 +└Output field names ["i" "j" "k"] +┌Filter on i > 0 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i" "j" "k"] +┌Order by i, j, +└Output field names ["i" "j" "k"] + +---- 1323 +SELECT * FROM t WHERE i > 0 && j > 0 ORDER BY i, j; +┌Iterate all rows of table "t" using index "xi" where i > 0 +└Output field names ["i" "j" "k"] +┌Filter on j > 0 +└Output field names ["i" "j" "k"] +┌Order by i, j, +└Output field names ["i" "j" "k"] + +---- 1324 +SELECT * FROM t WHERE j > 0 && i > 0 ORDER BY i, j; +┌Iterate all rows of table "t" using index "xj" where j > 0 +└Output field names ["i" "j" "k"] +┌Filter on i > 0 +└Output field names ["i" "j" "k"] +┌Order by i, j, +└Output field names ["i" "j" "k"] + +---- 1325 +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0; +┌Iterate all rows of table "t" +└Output field names ["i" "j" "k"] +┌Filter on i > 0 && j > 0 && k > 0 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +│CREATE INDEX xt_j ON t(j); +│CREATE INDEX xt_k ON t(k); +└Output field names ["i" "j" "k"] + +---- 1326 +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0; +┌Iterate all rows of table "t" using index "xi" where i > 0 +└Output field names ["i" "j" "k"] +┌Filter on j > 0 && k > 0 +└Output field names ["i" "j" "k"] + +---- 1327 +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0; +┌Iterate all rows of table "t" using index "xj" where j > 0 +└Output field names ["i" "j" "k"] +┌Filter on i > 0 && k > 0 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i" "j" "k"] + +---- 1328 +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0; +┌Iterate all rows of table "t" using index "xk" where k > 0 +└Output field names ["i" "j" "k"] +┌Filter on i > 0 && j > 0 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +│CREATE INDEX xt_j ON t(j); +└Output field names ["i" "j" "k"] + +---- 1329 +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +┌Iterate all rows of table "t" +└Output field names ["i" "j" "k" "l"] +┌Filter on i > 0 && j > 0 && k > 0 && l > 0 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +│CREATE INDEX xt_j ON t(j); +│CREATE INDEX xt_k ON t(k); +│CREATE INDEX xt_l ON t(l); +└Output field names ["i" "j" "k" "l"] + +---- 1330 +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +┌Iterate all rows of table "t" using index "xi" where i > 0 +└Output field names ["i" "j" "k" "l"] +┌Filter on j > 0 && k > 0 && l > 0 +└Output field names ["i" "j" "k" "l"] + +---- 1331 +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +┌Iterate all rows of table "t" using index "xj" where j > 0 +└Output field names ["i" "j" "k" "l"] +┌Filter on i > 0 && k > 0 && l > 0 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i" "j" "k" "l"] + +---- 1332 +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +┌Iterate all rows of table "t" using index "xk" where k > 0 +└Output field names ["i" "j" "k" "l"] +┌Filter on i > 0 && j > 0 && l > 0 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +│CREATE INDEX xt_j ON t(j); +└Output field names ["i" "j" "k" "l"] + +---- 1333 +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +┌Iterate all rows of table "t" using index "xl" where l > 0 +└Output field names ["i" "j" "k" "l"] +┌Filter on i > 0 && j > 0 && k > 0 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +│CREATE INDEX xt_j ON t(j); +│CREATE INDEX xt_k ON t(k); +└Output field names ["i" "j" "k" "l"] + +---- 1334 +SELECT * FROM t WHERE i > 12 && i >= 10 && i <= 20 && i < 15 ORDER BY i; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Filter on i > 12 && i >= 10 && i <= 20 && i < 15 +│Possibly useful indices +│CREATE INDEX xt_i ON t(i); +└Output field names ["i"] +┌Order by i, +└Output field names ["i"] + +---- 1335 +SELECT * FROM t WHERE i > 12 && i >= 10 && i <= 20 && i < 42; +┌Iterate all rows of table "t" using index "xt_i" where i > 12 && i <= 20 +└Output field names ["i"] + +---- 1336 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] + +---- 1337 +SELECT * FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] + +---- 1340 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["t"] +┌Group by distinct rows +└Output field names ["t"] +┌Evaluate count() as "", +└Output field names [""] + +---- 1341 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Group by distinct rows +└Output field names ["i"] +┌Evaluate count() as "", +└Output field names [""] + +---- 1342 +SELECT count() FROM t; +┌Iterate all rows of table "t" +└Output field names ["i"] +┌Group by distinct rows +└Output field names ["i"] +┌Evaluate count() as "", +└Output field names [""] + diff --git a/Godeps/_workspace/src/github.com/cznic/ql/testdata.ql b/Godeps/_workspace/src/github.com/cznic/ql/testdata.ql new file mode 100644 index 000000000..2272a6623 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/ql/testdata.ql @@ -0,0 +1,15633 @@ +// Copyright (c) 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// vi:filetype=sql + +-- 0 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int); + INSERT INTO t VALUES(11, 22, 33); +COMMIT; +SELECT * FROM t; +|"c1", "c2", "c3" +[11 22 33] + +-- 1 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int); + CREATE TABLE t (c1 int); +COMMIT; +||table.*exists + +-- 2 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c1 int, c4 int); +COMMIT; +||duplicate column + +-- 3 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int); + ALTER TABLE t ADD c4 string; + INSERT INTO t VALUES (1, 2, 3, "foo"); +COMMIT; +SELECT * FROM t; +|"c1", "c2", "c3", "c4" +[1 2 3 foo] + +-- 4 +BEGIN TRANSACTION; + ALTER TABLE none ADD c1 int; +COMMIT; +||table .* not exist + +-- 5 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int); + ALTER TABLE t ADD c2 int; +COMMIT; +||column .* exists + +-- 6 +BEGIN TRANSACTION; + ALTER TABLE none DROP COLUMN c1; +COMMIT; +||table .* not exist + +-- 7 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int); + ALTER TABLE t DROP COLUMN c4; +COMMIT; +||column .* not exist + +-- 8 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int); + ALTER TABLE t DROP COLUMN c2; + INSERT INTO t VALUES (1, 2); +COMMIT; +SELECT * FROM t; +|"c1", "c3" +[1 2] + +-- 9 +BEGIN TRANSACTION; + DROP TABLE none; +COMMIT; +||table .* not exist + +-- 10 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int); + DROP TABLE t; +COMMIT; +SELECT * FROM t; +||table .* not exist + +-- 11 +BEGIN TRANSACTION; + INSERT INTO none VALUES (1, 2); +COMMIT; +||table .* not exist + +-- 12 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int); + INSERT INTO t VALUES (1); +COMMIT; +||expect + +-- 13 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int); + INSERT INTO t VALUES (1, 2, 3); +COMMIT; +||expect + +-- 14 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int); + INSERT INTO t VALUES (1, 2/(3*5-15)); +COMMIT; +||division by zero + +-- 15 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int); + INSERT INTO t VALUES (2+3*4, 2*3+4); +COMMIT; +SELECT * FROM t; +|"c1", "c2" +[14 10] + +-- 16 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int, c4 int); + INSERT INTO t (c2, c4) VALUES (1); +COMMIT; +||expect + +-- 17 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int, c4 int); + INSERT INTO t (c2, c4) VALUES (1, 2, 3); +COMMIT; +||expect + +-- 18 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int, c4 int); + INSERT INTO t (c2, none) VALUES (1, 2); +COMMIT; +||unknown + +-- 19 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int, c4 int); + INSERT INTO t (c2, c3) VALUES (2+3*4, 2*3+4); + INSERT INTO t VALUES (1, 2, 3, 4, ); +COMMIT; +SELECT * FROM t; +|"c1", "c2", "c3", "c4" +[1 2 3 4] +[ 14 10 ] + +-- 20 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 int, c3 int, c4 int); + ALTER TABLE t DROP COLUMN c3; + INSERT INTO t (c1, c4) VALUES (42, 314); + INSERT INTO t (c1, c2) VALUES (2+3*4, 2*3+4); + INSERT INTO t VALUES (1, 2, 3); +COMMIT; +SELECT * FROM t; +|"c1", "c2", "c4" +[1 2 3] +[14 10 ] +[42 314] + +-- 21 +BEGIN TRANSACTION; + TRUNCATE TABLE none; +COMMIT; +||table .* not exist + +-- 22 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int); + INSERT INTO t VALUES(278); + TRUNCATE TABLE t; + INSERT INTO t VALUES(314); +COMMIT; +SELECT * FROM t; +|"c1" +[314] + +-- 23 +SELECT * FROM none; +||table .* not exist + +-- 24 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (2, "b"); + INSERT INTO t VALUES (1, "a"); +COMMIT; +SELECT * FROM t; +|"c1", "c2" +[1 a] +[2 b] + +-- 25 +SELECT c1 FROM none; +||table .* not exist + +-- 26 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (2, "b"); +COMMIT; +SELECT none FROM t; +||unknown + +-- 27 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (2, "b"); +COMMIT; +SELECT c1, none, c2 FROM t; +||unknown + +-- 28 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int32, c2 string); + INSERT INTO t VALUES (2, "b"); + INSERT INTO t VALUES (1, "a"); +COMMIT; +SELECT 3*c1 AS v FROM t; +|"v" +[3] +[6] + +-- 29 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (2, "b"); + INSERT INTO t VALUES (1, "a"); +COMMIT; +SELECT c2 FROM t; +|"c2" +[a] +[b] + +-- 30 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (2, "b"); + INSERT INTO t VALUES (1, "a"); +COMMIT; +SELECT c1 AS X, c2 FROM t; +|"X", "c2" +[1 a] +[2 b] + +-- 31 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (2, "b"); + INSERT INTO t VALUES (1, "a"); +COMMIT; +SELECT c2, c1 AS Y FROM t; +|"c2", "Y" +[a 1] +[b 2] + +-- 32 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (2, "b"); +COMMIT; +SELECT * FROM t WHERE c3 == 1; +||unknown + +-- 33 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (2, "b"); +COMMIT; +SELECT * FROM t WHERE c1 == 1; +|"c1", "c2" +[1 a] + +-- 34 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (2, "b"); +COMMIT; +SELECT * FROM t ORDER BY c3; +||unknown + +-- 35 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int32, c2 string); + INSERT INTO t VALUES (22, "bc"); + INSERT INTO t VALUES (11, "ab"); + INSERT INTO t VALUES (33, "cd"); +COMMIT; +SELECT * FROM t ORDER BY c1; +|"c1", "c2" +[11 ab] +[22 bc] +[33 cd] + +-- 36 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (2, "b"); +COMMIT; +SELECT * FROM t ORDER BY c1 ASC; +|"c1", "c2" +[1 a] +[2 b] + +-- 37 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (2, "b"); +COMMIT; +SELECT * FROM t ORDER BY c1 DESC; +|"c1", "c2" +[2 b] +[1 a] + +-- 38 +BEGIN TRANSACTION; +CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (2, "b"); + INSERT INTO t VALUES (3, "c"); + INSERT INTO t VALUES (4, "d"); + INSERT INTO t VALUES (5, "e"); + INSERT INTO t VALUES (6, "f"); + INSERT INTO t VALUES (7, "g"); +COMMIT; +SELECT * FROM t +WHERE c1 % 2 == 0 +ORDER BY c2 DESC; +|"c1", "c2" +[6 f] +[4 d] +[2 b] + +-- 39 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (2, "a"); + INSERT INTO t VALUES (3, "b"); + INSERT INTO t VALUES (4, "b"); + INSERT INTO t VALUES (5, "c"); + INSERT INTO t VALUES (6, "c"); + INSERT INTO t VALUES (7, "d"); +COMMIT; +SELECT * FROM t +ORDER BY c1, c2; +|"c1", "c2" +[1 a] +[2 a] +[3 b] +[4 b] +[5 c] +[6 c] +[7 d] + +-- 40 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int, c2 string); + INSERT INTO t VALUES (1, "d"); + INSERT INTO t VALUES (2, "c"); + INSERT INTO t VALUES (3, "c"); + INSERT INTO t VALUES (4, "b"); + INSERT INTO t VALUES (5, "b"); + INSERT INTO t VALUES (6, "a"); + INSERT INTO t VALUES (7, "a"); +COMMIT; +SELECT * FROM t +ORDER BY c2, c1 +|"c1", "c2" +[6 a] +[7 a] +[4 b] +[5 b] +[2 c] +[3 c] +[1 d] + +-- S 41 +SELECT * FROM employee, none; +||table .* not exist + +-- S 42 +SELECT employee.LastName FROM employee, none; +||table .* not exist + +-- S 43 +SELECT none FROM employee, department; +||unknown + +-- S 44 +SELECT employee.LastName FROM employee, department; +|"employee.LastName" +[Williams] +[Williams] +[Williams] +[Williams] +[Smith] +[Smith] +[Smith] +[Smith] +[Robinson] +[Robinson] +[Robinson] +[Robinson] +[Heisenberg] +[Heisenberg] +[Heisenberg] +[Heisenberg] +[Jones] +[Jones] +[Jones] +[Jones] +[Rafferty] +[Rafferty] +[Rafferty] +[Rafferty] + +-- S 45 +SELECT * FROM employee, department +ORDER by employee.LastName; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[Heisenberg 33 35 Marketing] +[Heisenberg 33 34 Clerical] +[Heisenberg 33 33 Engineering] +[Heisenberg 33 31 Sales] +[Jones 33 35 Marketing] +[Jones 33 34 Clerical] +[Jones 33 33 Engineering] +[Jones 33 31 Sales] +[Rafferty 31 35 Marketing] +[Rafferty 31 34 Clerical] +[Rafferty 31 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 35 Marketing] +[Robinson 34 34 Clerical] +[Robinson 34 33 Engineering] +[Robinson 34 31 Sales] +[Smith 34 35 Marketing] +[Smith 34 34 Clerical] +[Smith 34 33 Engineering] +[Smith 34 31 Sales] +[Williams 35 Marketing] +[Williams 34 Clerical] +[Williams 33 Engineering] +[Williams 31 Sales] + +-- S 46 +SELECT * +FROM employee, department +WHERE employee.DepartmentID == department.DepartmentID; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[Smith 34 34 Clerical] +[Robinson 34 34 Clerical] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] + +-- S 47 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID +FROM employee, department +WHERE employee.DepartmentID == department.DepartmentID +ORDER BY department.DepartmentName, employee.LastName; +|"department.DepartmentName", "department.DepartmentID", "employee.LastName", "employee.DepartmentID" +[Clerical 34 Robinson 34] +[Clerical 34 Smith 34] +[Engineering 33 Heisenberg 33] +[Engineering 33 Jones 33] +[Sales 31 Rafferty 31] + +-- S 48 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID +FROM employee, department +WHERE department.DepartmentName IN ("Sales", "Engineering", "HR", "Clerical") +ORDER BY employee.LastName; +|"department.DepartmentName", "department.DepartmentID", "employee.LastName", "employee.DepartmentID" +[Clerical 34 Heisenberg 33] +[Engineering 33 Heisenberg 33] +[Sales 31 Heisenberg 33] +[Clerical 34 Jones 33] +[Engineering 33 Jones 33] +[Sales 31 Jones 33] +[Clerical 34 Rafferty 31] +[Engineering 33 Rafferty 31] +[Sales 31 Rafferty 31] +[Clerical 34 Robinson 34] +[Engineering 33 Robinson 34] +[Sales 31 Robinson 34] +[Clerical 34 Smith 34] +[Engineering 33 Smith 34] +[Sales 31 Smith 34] +[Clerical 34 Williams ] +[Engineering 33 Williams ] +[Sales 31 Williams ] + +-- S 49 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID +FROM employee, department +WHERE (department.DepartmentID+1000) IN (1031, 1035, 1036) +ORDER BY employee.LastName; +|"department.DepartmentName", "department.DepartmentID", "employee.LastName", "employee.DepartmentID" +[Marketing 35 Heisenberg 33] +[Sales 31 Heisenberg 33] +[Marketing 35 Jones 33] +[Sales 31 Jones 33] +[Marketing 35 Rafferty 31] +[Sales 31 Rafferty 31] +[Marketing 35 Robinson 34] +[Sales 31 Robinson 34] +[Marketing 35 Smith 34] +[Sales 31 Smith 34] +[Marketing 35 Williams ] +[Sales 31 Williams ] + +-- S 50 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID +FROM employee, department +WHERE department.DepartmentName NOT IN ("Engineering", "HR", "Clerical"); +|"department.DepartmentName", "department.DepartmentID", "employee.LastName", "employee.DepartmentID" +[Marketing 35 Williams ] +[Sales 31 Williams ] +[Marketing 35 Smith 34] +[Sales 31 Smith 34] +[Marketing 35 Robinson 34] +[Sales 31 Robinson 34] +[Marketing 35 Heisenberg 33] +[Sales 31 Heisenberg 33] +[Marketing 35 Jones 33] +[Sales 31 Jones 33] +[Marketing 35 Rafferty 31] +[Sales 31 Rafferty 31] + +-- S 51 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID +FROM employee, department +WHERE department.DepartmentID BETWEEN 34 AND 36 +ORDER BY employee.LastName; +|"department.DepartmentName", "department.DepartmentID", "employee.LastName", "employee.DepartmentID" +[Marketing 35 Heisenberg 33] +[Clerical 34 Heisenberg 33] +[Marketing 35 Jones 33] +[Clerical 34 Jones 33] +[Marketing 35 Rafferty 31] +[Clerical 34 Rafferty 31] +[Marketing 35 Robinson 34] +[Clerical 34 Robinson 34] +[Marketing 35 Smith 34] +[Clerical 34 Smith 34] +[Marketing 35 Williams ] +[Clerical 34 Williams ] + +-- S 52 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID +FROM employee, department +WHERE department.DepartmentID BETWEEN int64(34) AND int64(36) +ORDER BY employee.LastName; +|"department.DepartmentName", "department.DepartmentID", "employee.LastName", "employee.DepartmentID" +[Marketing 35 Heisenberg 33] +[Clerical 34 Heisenberg 33] +[Marketing 35 Jones 33] +[Clerical 34 Jones 33] +[Marketing 35 Rafferty 31] +[Clerical 34 Rafferty 31] +[Marketing 35 Robinson 34] +[Clerical 34 Robinson 34] +[Marketing 35 Smith 34] +[Clerical 34 Smith 34] +[Marketing 35 Williams ] +[Clerical 34 Williams ] + +-- S 53 +SELECT department.DepartmentName, department.DepartmentID, employee.LastName, employee.DepartmentID +FROM employee, department +WHERE department.DepartmentID NOT BETWEEN 33 AND 34 //TODO plan for 'or' in this case is possible. +ORDER BY employee.LastName; +|"department.DepartmentName", "department.DepartmentID", "employee.LastName", "employee.DepartmentID" +[Marketing 35 Heisenberg 33] +[Sales 31 Heisenberg 33] +[Marketing 35 Jones 33] +[Sales 31 Jones 33] +[Marketing 35 Rafferty 31] +[Sales 31 Rafferty 31] +[Marketing 35 Robinson 34] +[Sales 31 Robinson 34] +[Marketing 35 Smith 34] +[Sales 31 Smith 34] +[Marketing 35 Williams ] +[Sales 31 Williams ] + +-- S 54 +SELECT LastName, LastName FROM employee; +||duplicate + +-- S 55 +SELECT LastName+", " AS a, LastName AS a FROM employee; +||duplicate + +-- S 56 +SELECT LastName AS a, LastName AS b FROM employee +ORDER by a, b; +|"a", "b" +[Heisenberg Heisenberg] +[Jones Jones] +[Rafferty Rafferty] +[Robinson Robinson] +[Smith Smith] +[Williams Williams] + +-- S 57 +SELECT employee.LastName AS name, employee.DepartmentID AS id, department.DepartmentName AS department, department.DepartmentID AS id2 +FROM employee, department +WHERE employee.DepartmentID == department.DepartmentID +ORDER BY name, id, department, id2; +|"name", "id", "department", "id2" +[Heisenberg 33 Engineering 33] +[Jones 33 Engineering 33] +[Rafferty 31 Sales 31] +[Robinson 34 Clerical 34] +[Smith 34 Clerical 34] + +-- S 58 +SELECT * FROM; +||expected .*RecordSetList + +-- S 59 +SELECT * FROM employee +ORDER BY LastName; +|"LastName", "DepartmentID" +[Heisenberg 33] +[Jones 33] +[Rafferty 31] +[Robinson 34] +[Smith 34] +[Williams ] + +-- S 60 +SELECT * FROM employee AS e +ORDER BY LastName; +|"LastName", "DepartmentID" +[Heisenberg 33] +[Jones 33] +[Rafferty 31] +[Robinson 34] +[Smith 34] +[Williams ] + +-- S 61 +SELECT none FROM ( + SELECT * FROM employee; + SELECT * FROM department; +); +||expected .*'\)' + +-- S 62 +SELECT none FROM ( + SELECT * FROM employee; +); +||unknown + +-- S 63 +SELECT noneCol FROM ( + SELECT * FROM noneTab +); +||not exist + +-- S 64 +SELECT noneCol FROM ( + SELECT * FROM employee +); +||unknown + +-- S 65 +SELECT * FROM ( + SELECT * FROM employee +) +ORDER BY LastName; +|"LastName", "DepartmentID" +[Heisenberg 33] +[Jones 33] +[Rafferty 31] +[Robinson 34] +[Smith 34] +[Williams ] + +-- S 66 +SELECT * FROM ( + SELECT LastName AS Name FROM employee +) +ORDER BY Name; +|"Name" +[Heisenberg] +[Jones] +[Rafferty] +[Robinson] +[Smith] +[Williams] + +-- S 67 +SELECT Name FROM ( + SELECT LastName AS name FROM employee +); +||unknown + +-- S 68 +SELECT name AS Name FROM ( + SELECT LastName AS name + FROM employee AS e +) +ORDER BY Name; +|"Name" +[Heisenberg] +[Jones] +[Rafferty] +[Robinson] +[Smith] +[Williams] + +-- S 69 +SELECT name AS Name FROM ( + SELECT LastName AS name FROM employee +) +ORDER BY Name; +|"Name" +[Heisenberg] +[Jones] +[Rafferty] +[Robinson] +[Smith] +[Williams] + +-- S 70 +SELECT employee.LastName, department.DepartmentName, department.DepartmentID FROM ( + SELECT * + FROM employee, department + WHERE employee.DepartmentID == department.DepartmentID +) +ORDER BY department.DepartmentName, employee.LastName +|"employee.LastName", "department.DepartmentName", "department.DepartmentID" +[Robinson Clerical 34] +[Smith Clerical 34] +[Heisenberg Engineering 33] +[Jones Engineering 33] +[Rafferty Sales 31] + +-- S 71 +SELECT e.LastName, d.DepartmentName, d.DepartmentID FROM ( + SELECT * + FROM employee AS e, department AS d + WHERE e.DepartmentID == d.DepartmentID +) +ORDER by d.DepartmentName, e.LastName; +|"e.LastName", "d.DepartmentName", "d.DepartmentID" +[Robinson Clerical 34] +[Smith Clerical 34] +[Heisenberg Engineering 33] +[Jones Engineering 33] +[Rafferty Sales 31] + +-- S 72 +SELECT e.LastName AS name, d.DepartmentName AS department, d.DepartmentID AS id FROM ( + SELECT * + FROM employee AS e, department AS d + WHERE e.DepartmentID == d.DepartmentID +) +ORDER by department, name +|"name", "department", "id" +[Robinson Clerical 34] +[Smith Clerical 34] +[Heisenberg Engineering 33] +[Jones Engineering 33] +[Rafferty Sales 31] + +-- S 73 +SELECT name, department, id FROM ( + SELECT e.LastName AS name, e.DepartmentID AS id, d.DepartmentName AS department, d.DepartmentID AS fid + FROM employee AS e, department AS d + WHERE e.DepartmentID == d.DepartmentID +) +ORDER by department, name; +|"name", "department", "id" +[Robinson Clerical 34] +[Smith Clerical 34] +[Heisenberg Engineering 33] +[Jones Engineering 33] +[Rafferty Sales 31] + +-- S 74 +SELECT * +FROM +( + SELECT * + FROM employee +), +( + SELECT * + FROM department +); +|"", "", "", "" +[Williams 35 Marketing] +[Williams 34 Clerical] +[Williams 33 Engineering] +[Williams 31 Sales] +[Smith 34 35 Marketing] +[Smith 34 34 Clerical] +[Smith 34 33 Engineering] +[Smith 34 31 Sales] +[Robinson 34 35 Marketing] +[Robinson 34 34 Clerical] +[Robinson 34 33 Engineering] +[Robinson 34 31 Sales] +[Heisenberg 33 35 Marketing] +[Heisenberg 33 34 Clerical] +[Heisenberg 33 33 Engineering] +[Heisenberg 33 31 Sales] +[Jones 33 35 Marketing] +[Jones 33 34 Clerical] +[Jones 33 33 Engineering] +[Jones 33 31 Sales] +[Rafferty 31 35 Marketing] +[Rafferty 31 34 Clerical] +[Rafferty 31 33 Engineering] +[Rafferty 31 31 Sales] + +-- S 75 +SELECT * +FROM +( + SELECT * + FROM employee +) AS e, +( + SELECT * + FROM department +) +ORDER BY e.LastName, e.DepartmentID; +|"e.LastName", "e.DepartmentID", "", "" +[Heisenberg 33 35 Marketing] +[Heisenberg 33 34 Clerical] +[Heisenberg 33 33 Engineering] +[Heisenberg 33 31 Sales] +[Jones 33 35 Marketing] +[Jones 33 34 Clerical] +[Jones 33 33 Engineering] +[Jones 33 31 Sales] +[Rafferty 31 35 Marketing] +[Rafferty 31 34 Clerical] +[Rafferty 31 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 35 Marketing] +[Robinson 34 34 Clerical] +[Robinson 34 33 Engineering] +[Robinson 34 31 Sales] +[Smith 34 35 Marketing] +[Smith 34 34 Clerical] +[Smith 34 33 Engineering] +[Smith 34 31 Sales] +[Williams 35 Marketing] +[Williams 34 Clerical] +[Williams 33 Engineering] +[Williams 31 Sales] + +-- S 76 +SELECT * +FROM +( + SELECT * + FROM employee +), +( + SELECT * + FROM department +) AS d +ORDER BY d.DepartmentID DESC; +|"", "", "d.DepartmentID", "d.DepartmentName" +[Rafferty 31 35 Marketing] +[Jones 33 35 Marketing] +[Heisenberg 33 35 Marketing] +[Robinson 34 35 Marketing] +[Smith 34 35 Marketing] +[Williams 35 Marketing] +[Rafferty 31 34 Clerical] +[Jones 33 34 Clerical] +[Heisenberg 33 34 Clerical] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] +[Williams 34 Clerical] +[Rafferty 31 33 Engineering] +[Jones 33 33 Engineering] +[Heisenberg 33 33 Engineering] +[Robinson 34 33 Engineering] +[Smith 34 33 Engineering] +[Williams 33 Engineering] +[Rafferty 31 31 Sales] +[Jones 33 31 Sales] +[Heisenberg 33 31 Sales] +[Robinson 34 31 Sales] +[Smith 34 31 Sales] +[Williams 31 Sales] + +-- S 77 +SELECT * +FROM + employee, + ( + SELECT * + FROM department + ) +ORDER BY employee.LastName; +|"employee.LastName", "employee.DepartmentID", "", "" +[Heisenberg 33 35 Marketing] +[Heisenberg 33 34 Clerical] +[Heisenberg 33 33 Engineering] +[Heisenberg 33 31 Sales] +[Jones 33 35 Marketing] +[Jones 33 34 Clerical] +[Jones 33 33 Engineering] +[Jones 33 31 Sales] +[Rafferty 31 35 Marketing] +[Rafferty 31 34 Clerical] +[Rafferty 31 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 35 Marketing] +[Robinson 34 34 Clerical] +[Robinson 34 33 Engineering] +[Robinson 34 31 Sales] +[Smith 34 35 Marketing] +[Smith 34 34 Clerical] +[Smith 34 33 Engineering] +[Smith 34 31 Sales] +[Williams 35 Marketing] +[Williams 34 Clerical] +[Williams 33 Engineering] +[Williams 31 Sales] + +-- S 78 +SELECT * +FROM +( + SELECT * + FROM employee +) AS e, +( + SELECT * + FROM department +) AS d +WHERE e.DepartmentID == d.DepartmentID +ORDER BY d.DepartmentName, e.LastName; +|"e.LastName", "e.DepartmentID", "d.DepartmentID", "d.DepartmentName" +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] + +-- S 79 +SELECT * +FROM + employee, + ( + SELECT * + FROM department + ) AS d +WHERE employee.DepartmentID == d.DepartmentID +ORDER BY d.DepartmentName, employee.LastName; +|"employee.LastName", "employee.DepartmentID", "d.DepartmentID", "d.DepartmentName" +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] + +-- S 80 +SELECT * +FROM + employee AS e, + ( + SELECT * + FROM department + ) AS d +WHERE e.DepartmentID == d.DepartmentID +ORDER BY d.DepartmentName, e.LastName; +|"e.LastName", "e.DepartmentID", "d.DepartmentID", "d.DepartmentName" +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] + +-- S 81 +SELECT * +FROM + employee AS e, + ( + SELECT * + FROM department + ) AS d +WHERE e.DepartmentID == d.DepartmentID == true +ORDER BY e.DepartmentID, e.LastName; +|"e.LastName", "e.DepartmentID", "d.DepartmentID", "d.DepartmentName" +[Rafferty 31 31 Sales] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] + +-- S 82 +SELECT * +FROM + employee AS e, + ( + SELECT * + FROM department + ) AS d +WHERE e.DepartmentID != d.DepartmentID == false +ORDER BY e.DepartmentID, e.LastName; +|"e.LastName", "e.DepartmentID", "d.DepartmentID", "d.DepartmentName" +[Rafferty 31 31 Sales] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] + +-- 83 +BEGIN TRANSACTION; + CREATE TABLE t (c1 bool); + INSERT INTO t VALUES (1); +COMMIT; +||type int64.*type bool + +-- 84 +BEGIN TRANSACTION; + CREATE TABLE t (c1 bool); + INSERT INTO t VALUES (true); +COMMIT; +SELECT * from t; +|"c1" +[true] + +-- 85 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int8); + INSERT INTO t VALUES ("foo"); +COMMIT; +||type string.*type int8 + +-- 86 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int8); + INSERT INTO t VALUES (0x1234); +COMMIT; +SELECT * from t; +||overflow + +-- 87 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int16); + INSERT INTO t VALUES (87); +COMMIT; +SELECT * from t; +|"c1" +[87] + +-- 88 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int16); + INSERT INTO t VALUES (int16(0x12345678)); +COMMIT; +SELECT * from t; +|"c1" +[22136] + +-- 89 +BEGIN TRANSACTION; +CREATE TABLE t (c1 int32); + INSERT INTO t VALUES (uint32(1)); +COMMIT; +||type uint32.*type int32 + +-- 90 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int32); + INSERT INTO t VALUES (0xabcd12345678); +COMMIT; +SELECT * from t; +||overflow + +-- 91 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int64); + INSERT INTO t VALUES (int8(1)); +COMMIT; +||type int8.*type int64 + +-- 92 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int64); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t; +|"c1" +[1] + +-- 93 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int); + INSERT INTO t VALUES (int8(1)); +COMMIT; +||type int8.*type int64 + +-- 94 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int); + INSERT INTO t VALUES (94); +COMMIT; +SELECT * from t; +|"c1" +[94] + +-- 95 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint8); + INSERT INTO t VALUES (95); +COMMIT; +SELECT * from t; +|"c1" +[95] + +-- 96 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint8); + INSERT INTO t VALUES (uint8(0x1234)); +COMMIT; +SELECT * from t; +|"c1" +[52] + +-- 97 +BEGIN TRANSACTION; + CREATE TABLE t (c1 byte); + INSERT INTO t VALUES (int8(1)); +COMMIT; +||type int8.*type uint8 + +-- 98 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint8); + INSERT INTO t VALUES (byte(0x1234)); +COMMIT; +SELECT * from t; +|"c1" +[52] + +-- 99 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint16); + INSERT INTO t VALUES (int(1)); +COMMIT; +||type int64.*uint16 + +-- 100 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint16); + INSERT INTO t VALUES (0x12345678); +COMMIT; +SELECT * from t; +||overflow + +-- 101 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint32); + INSERT INTO t VALUES (int32(1)); +COMMIT; +||type int32.*type uint32 + +-- 102 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint32); + INSERT INTO t VALUES (uint32(0xabcd12345678)); +COMMIT; +SELECT * from t; +|"c1" +[305419896] + +-- 103 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint64); + INSERT INTO t VALUES (int(1)); +COMMIT; +||type int64.*type uint64 + +-- 104 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint64); + INSERT INTO t VALUES (uint64(1)); +COMMIT; +SELECT * from t; +|"c1" +[1] + +-- 105 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint); + INSERT INTO t VALUES (int(1)); +COMMIT; +||type int64.*type uint64 + +-- 106 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t; +|"c1" +[1] + +-- 107 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float32); + INSERT INTO t VALUES (107); +COMMIT; +SELECT * from t; +|"c1" +[107] + +-- 108 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float32); + INSERT INTO t VALUES (float64(1)); +COMMIT; +SELECT * from t; +||type float64.*type float32 + +-- 109 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float32); + INSERT INTO t VALUES (1.2); +COMMIT; +SELECT * from t; +|"c1" +[1.2] + +-- 110 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float64); + INSERT INTO t VALUES (1.2); +COMMIT; +SELECT * from t; +|"c1" +[1.2] + +-- 111 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float); + INSERT INTO t VALUES (111.1); +COMMIT; +SELECT * from t; +|"c1" +[111.1] + +-- 112 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float); + INSERT INTO t VALUES (-112.1); +COMMIT; +SELECT * from t; +|"c1" +[-112.1] + +-- 113 +BEGIN TRANSACTION; + CREATE TABLE t (c1 complex64); + INSERT INTO t VALUES (complex(1, 0.5)); +COMMIT; +SELECT * from t; +|"c1" +[(1+0.5i)] + +-- 114 +BEGIN TRANSACTION; + CREATE TABLE t (c1 complex64); + INSERT INTO t VALUES (complex128(complex(1, 0.5))); +COMMIT; +SELECT * from t; +||type complex128.*type complex64 + +-- 115 +BEGIN TRANSACTION; + CREATE TABLE t (c1 complex128); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t; +|"c1" +[(1+0i)] + +-- 116 +BEGIN TRANSACTION; + CREATE TABLE t (c1 complex128); + INSERT INTO t VALUES (complex(1, 0.5)); +COMMIT; +SELECT * from t; +|"c1" +[(1+0.5i)] + +-- 117 +BEGIN TRANSACTION; + CREATE TABLE t (c1 string); + INSERT INTO t VALUES (1); +COMMIT; +||type int64.*type string + +-- 118 +BEGIN TRANSACTION; + CREATE TABLE t (c1 string); + INSERT INTO t VALUES ("a"+"b"); +COMMIT; +SELECT * from t; +|"c1" +[ab] + +-- 119 +BEGIN TRANSACTION; + CREATE TABLE t (c1 bool); + INSERT INTO t VALUES (true); +COMMIT; +SELECT * from t +WHERE c1 > 3; +||operator .* not defined .* bool + +-- 120 +BEGIN TRANSACTION; + CREATE TABLE t (c1 bool); + INSERT INTO t VALUES (true); +COMMIT; +SELECT * from t +WHERE c1; +|"c1" +[true] + +-- 121 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int8); + INSERT INTO t VALUES (float(1)); +COMMIT; +SELECT * from t +WHERE c1 == 8; +||type float64.*type int8 + +-- 122 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int8); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == int8(1); +|"c1" +[1] + +-- 123 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int16); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == int(8); +||mismatched .* int16 .* int64 + +-- 124 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int16); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == 1; +|"c1" +[1] + +-- 125 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int32); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == int(8); +||mismatched .* int32 .* int64 + +-- 126 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int32); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == 1; +|"c1" +[1] + +-- 127 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int64); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == byte(8); +||mismatched .* int64 .* uint8 + +-- 128 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int64); + INSERT INTO t VALUES (int64(1)); +COMMIT; +SELECT * from t +WHERE c1 == 1; +|"c1" +[1] + +-- 129 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == 1; +|"c1" +[1] + +-- 130 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint8); + INSERT INTO t VALUES (byte(1)); +COMMIT; +SELECT * from t +WHERE c1 == int8(8); +||mismatched .* uint8 .* int8 + +-- 131 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint8); + INSERT INTO t VALUES (byte(1)); +COMMIT; +SELECT * from t +WHERE c1 == 1; +|"c1" +[1] + +-- 132 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint16); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == byte(8); +||mismatched .* uint16 .* uint8 + +-- 133 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint16); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == 1; +|"c1" +[1] + +-- 134 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint32); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == 1; +|"c1" +[1] + +-- 135 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint64); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == int(8); +||mismatched .* uint64 .* int64 + +-- 136 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint64); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == 1; +|"c1" +[1] + +-- 137 +BEGIN TRANSACTION; + CREATE TABLE t (c1 uint); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * from t +WHERE c1 == 1; +|"c1" +[1] + +-- 138 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float32); + INSERT INTO t VALUES (8); +COMMIT; +SELECT * from t +WHERE c1 == byte(8); +||mismatched .* float32 .* uint8 + +-- 139 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float32); + INSERT INTO t VALUES (8); +COMMIT; +SELECT * from t +WHERE c1 == 8; +|"c1" +[8] + +-- 140 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float64); + INSERT INTO t VALUES (2); +COMMIT; +SELECT * from t +WHERE c1 == byte(2); +||mismatched .* float64 .* uint8 + +-- 141 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float64); + INSERT INTO t VALUES (2); +COMMIT; +SELECT * from t +WHERE c1 == 2; +|"c1" +[2] + +-- 142 +BEGIN TRANSACTION; + CREATE TABLE t (c1 float); + INSERT INTO t VALUES (2.); +COMMIT; +SELECT * from t +WHERE c1 == 2; +|"c1" +[2] + +-- 143 +BEGIN TRANSACTION; + CREATE TABLE t (c1 complex64); + INSERT INTO t VALUES (complex(2., 5.)); +COMMIT; +SELECT * from t +WHERE c1 == "foo"; +||mismatched .* complex64 .* string + +-- 144 +BEGIN TRANSACTION; + CREATE TABLE t (c1 complex64); + INSERT INTO t VALUES (complex(2, 5.)); +COMMIT; +SELECT * from t +WHERE c1 == 2+5i; +|"c1" +[(2+5i)] + +-- 145 +BEGIN TRANSACTION; + CREATE TABLE t (c1 complex128); + INSERT INTO t VALUES (2+5i); +COMMIT; +SELECT * from t +WHERE c1 == "2"; +||mismatched .* complex128 .* string + +-- 146 +BEGIN TRANSACTION; + CREATE TABLE t (c1 complex128); + INSERT INTO t VALUES (2+5i); +COMMIT; +SELECT * from t +WHERE c1 == complex(2, 5); +|"c1" +[(2+5i)] + +-- 147 +BEGIN TRANSACTION; + CREATE TABLE t (c1 string); + INSERT INTO t VALUES ("foo"); +COMMIT; +SELECT * from t +WHERE c1 == 2; +||mismatched .* string .* int64 + +-- 148 +BEGIN TRANSACTION; + CREATE TABLE t (c1 string); + INSERT INTO t VALUES ("f"+"oo"); +COMMIT; +SELECT * from t +WHERE c1 == "fo"+"o"; +|"c1" +[foo] + +-- 149 +SELECT 2/(3*5-15) AS foo FROM bar; +||division by zero + +-- 150 +SELECT 2.0/(2.0-2.0) AS foo FROM bar; +||division by zero + +-- 151 +SELECT 2i/(2i-2i) AS foo FROM bar; +||division by zero + +-- 152 +SELECT 2/(3*5-x) AS foo FROM bar; +||table .* not exist + +-- S 153 +SELECT 314, 42 AS AUQLUE, DepartmentID, DepartmentID+1000, LastName AS Name +FROM employee +ORDER BY Name; +|"", "AUQLUE", "DepartmentID", "", "Name" +[314 42 33 1033 Heisenberg] +[314 42 33 1033 Jones] +[314 42 31 1031 Rafferty] +[314 42 34 1034 Robinson] +[314 42 34 1034 Smith] +[314 42 Williams] + +-- S 154 +SELECT * +FROM + employee AS e, + ( SELECT * FROM department) +ORDER BY e.LastName; +|"e.LastName", "e.DepartmentID", "", "" +[Heisenberg 33 35 Marketing] +[Heisenberg 33 34 Clerical] +[Heisenberg 33 33 Engineering] +[Heisenberg 33 31 Sales] +[Jones 33 35 Marketing] +[Jones 33 34 Clerical] +[Jones 33 33 Engineering] +[Jones 33 31 Sales] +[Rafferty 31 35 Marketing] +[Rafferty 31 34 Clerical] +[Rafferty 31 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 35 Marketing] +[Robinson 34 34 Clerical] +[Robinson 34 33 Engineering] +[Robinson 34 31 Sales] +[Smith 34 35 Marketing] +[Smith 34 34 Clerical] +[Smith 34 33 Engineering] +[Smith 34 31 Sales] +[Williams 35 Marketing] +[Williams 34 Clerical] +[Williams 33 Engineering] +[Williams 31 Sales] + +-- S 155 +SELECT * FROM employee AS e, ( SELECT * FROM department) AS d +ORDER BY e.LastName; +|"e.LastName", "e.DepartmentID", "d.DepartmentID", "d.DepartmentName" +[Heisenberg 33 35 Marketing] +[Heisenberg 33 34 Clerical] +[Heisenberg 33 33 Engineering] +[Heisenberg 33 31 Sales] +[Jones 33 35 Marketing] +[Jones 33 34 Clerical] +[Jones 33 33 Engineering] +[Jones 33 31 Sales] +[Rafferty 31 35 Marketing] +[Rafferty 31 34 Clerical] +[Rafferty 31 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 35 Marketing] +[Robinson 34 34 Clerical] +[Robinson 34 33 Engineering] +[Robinson 34 31 Sales] +[Smith 34 35 Marketing] +[Smith 34 34 Clerical] +[Smith 34 33 Engineering] +[Smith 34 31 Sales] +[Williams 35 Marketing] +[Williams 34 Clerical] +[Williams 33 Engineering] +[Williams 31 Sales] + +-- 156 +BEGIN TRANSACTION; + CREATE TABLE t (c1 int32, c2 string); + INSERT INTO t VALUES (1, "a"); + INSERT INTO t VALUES (int64(2), "b"); +COMMIT; +SELECT c2 FROM t; +||type int64.*type int32 + +-- 157 +BEGIN TRANSACTION; + CREATE TABLE t (c1 complex64); + INSERT INTO t VALUES(1); +COMMIT; +SELECT * FROM t; +|"c1" +[(1+0i)] + +-- 158 +BEGIN TRANSACTION; + CREATE TABLE p (p bool); + INSERT INTO p VALUES (NULL), (false), (true); +COMMIT; +SELECT * FROM p; +|"p" +[true] +[false] +[] + +-- 159 +BEGIN TRANSACTION; + CREATE TABLE p (p bool); + INSERT INTO p VALUES (NULL), (false), (true); +COMMIT; +SELECT p.p AS p, q.p AS q, p.p OR q.p AS p_or_q, p.p && q.p aS p_and_q FROM p, p AS q; +|"p", "q", "p_or_q", "p_and_q" +[true true true true] +[true false true false] +[true true ] +[false true true false] +[false false false false] +[false false] +[ true true ] +[ false false] +[ ] + +-- 160 +BEGIN TRANSACTION; + CREATE TABLE p (p bool); + INSERT INTO p VALUES (NULL), (false), (true); +COMMIT; +SELECT p, !p AS not_p FROM p; +|"p", "not_p" +[true false] +[false true] +[ ] + +-- S 161 +SELECT * FROM department WHERE DepartmentID >= 33 +ORDER BY DepartmentID; +|"DepartmentID", "DepartmentName" +[33 Engineering] +[34 Clerical] +[35 Marketing] + +-- S 162 +SELECT * FROM department WHERE DepartmentID <= 34 +ORDER BY DepartmentID; +|"DepartmentID", "DepartmentName" +[31 Sales] +[33 Engineering] +[34 Clerical] + +-- S 163 +SELECT * FROM department WHERE DepartmentID < 34 +ORDER BY DepartmentID; +|"DepartmentID", "DepartmentName" +[31 Sales] +[33 Engineering] + +-- S 164 +SELECT +DepartmentID FROM employee; +|"" +[] +[34] +[34] +[33] +[33] +[31] + +-- S 165 +SELECT * FROM employee +ORDER BY LastName; +|"LastName", "DepartmentID" +[Heisenberg 33] +[Jones 33] +[Rafferty 31] +[Robinson 34] +[Smith 34] +[Williams ] + +-- S 166 +SELECT * +FROM employee +ORDER BY LastName DESC; +|"LastName", "DepartmentID" +[Williams ] +[Smith 34] +[Robinson 34] +[Rafferty 31] +[Jones 33] +[Heisenberg 33] + +-- S 167 +SELECT 1023+DepartmentID AS y FROM employee +ORDER BY y DESC; +|"y" +[1057] +[1057] +[1056] +[1056] +[1054] +[] + +-- S 168 +SELECT +DepartmentID AS y FROM employee +ORDER BY y DESC; +|"y" +[34] +[34] +[33] +[33] +[31] +[] + +-- S 169 +SELECT * FROM employee ORDER BY DepartmentID, LastName DESC; +|"LastName", "DepartmentID" +[Smith 34] +[Robinson 34] +[Jones 33] +[Heisenberg 33] +[Rafferty 31] +[Williams ] + +-- S 170 +SELECT * FROM employee ORDER BY 0+DepartmentID DESC; +|"LastName", "DepartmentID" +[Robinson 34] +[Smith 34] +[Jones 33] +[Heisenberg 33] +[Rafferty 31] +[Williams ] + +-- S 171 +SELECT * FROM employee ORDER BY +DepartmentID DESC; +|"LastName", "DepartmentID" +[Robinson 34] +[Smith 34] +[Jones 33] +[Heisenberg 33] +[Rafferty 31] +[Williams ] + +-- S 172 +SELECT ^DepartmentID AS y FROM employee +ORDER BY y DESC; +|"y" +[-32] +[-34] +[-34] +[-35] +[-35] +[] + +-- S 173 +SELECT ^byte(DepartmentID) AS y FROM employee ORDER BY y DESC; +|"y" +[224] +[222] +[222] +[221] +[221] +[] + +-- 174 +BEGIN TRANSACTION; + CREATE TABLE t (r RUNE); + INSERT INTO t VALUES (1), ('A'), (rune(int(0x21))); +COMMIT; +SELECT * FROM t +ORDER BY r; +|"r" +[1] +[33] +[65] + +-- 175 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (-2), (-1), (0), (1), (2); +COMMIT; +SELECT i^1 AS y FROM t +ORDER by y; +|"y" +[-2] +[-1] +[0] +[1] +[3] + +-- 176 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (-2), (-1), (0), (1), (2); +COMMIT; +SELECT i∨1 AS y FROM t +ORDER BY y; +|"y" +[-1] +[-1] +[1] +[1] +[3] + +-- 177 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (-2), (-1), (0), (1), (2); +COMMIT; +SELECT i&1 FROM t; +|"" +[0] +[1] +[0] +[1] +[0] + +-- 178 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (-2), (-1), (0), (1), (2); +COMMIT; +SELECT i&^1 AS y FROM t +ORDER BY y; +|"y" +[-2] +[-2] +[0] +[0] +[2] + +-- S 179 +SELECT * from employee WHERE LastName == "Jones" OR DepartmentID IS NULL +ORDER by LastName DESC; +|"LastName", "DepartmentID" +[Williams ] +[Jones 33] + +-- S 180 +SELECT * from employee WHERE LastName != "Jones" && DepartmentID IS NOT NULL +ORDER BY LastName; +|"LastName", "DepartmentID" +[Heisenberg 33] +[Rafferty 31] +[Robinson 34] +[Smith 34] + +-- 181 +SELECT 42[0] FROM foo; +||invalid operation.*index of type + +-- 182 +SELECT "foo"[-1] FROM foo; +||invalid string index.*index .* non.*negative + +-- 183 +SELECT "foo"[3] FROM foo; +||invalid string index.*out of bounds + +-- 184 +BEGIN TRANSACTION; + CREATE TABLE foo(i int); +COMMIT; +SELECT "foo"["bar">true] FROM foo; +||mismatched types + +-- S 185 +SELECT DepartmentID[0] FROM employee; +||run.time error.*invalid operation.*index of type + +-- S 186 +SELECT "foo"[-DepartmentID] FROM employee; +||run.time error.*invalid string index.*index .* non.*negative + +-- S 187 +SELECT LastName[100] FROM employee; +||run.time.error.*invalid string index.*out of bounds + +-- S 188 +SELECT LastName[0], LastName FROM employee ORDER BY LastName; +|"", "LastName" +[72 Heisenberg] +[74 Jones] +[82 Rafferty] +[82 Robinson] +[83 Smith] +[87 Williams] + +-- S 189 +SELECT LastName, string(LastName[0]), string(LastName[1]), string(LastName[2]), string(LastName[3]) +FROM employee +ORDER BY LastName; +|"LastName", "", "", "", "" +[Heisenberg H e i s] +[Jones J o n e] +[Rafferty R a f f] +[Robinson R o b i] +[Smith S m i t] +[Williams W i l l] + +-- S 190 +SELECT LastName, LastName[:], LastName[:2], LastName[2:], LastName[1:3] +FROM employee +ORDER by LastName; +|"LastName", "", "", "", "" +[Heisenberg Heisenberg He isenberg ei] +[Jones Jones Jo nes on] +[Rafferty Rafferty Ra fferty af] +[Robinson Robinson Ro binson ob] +[Smith Smith Sm ith mi] +[Williams Williams Wi lliams il] + +-- S 191 +SELECT LastName +FROM employee +WHERE department IS NULL; +||unknown field department + +-- S 192 +SELECT + DepartmentID, + LastName, + LastName[:4], + LastName[:0*DepartmentID], + LastName[0*DepartmentID:0], + LastName[0*DepartmentID:0*DepartmentID], +FROM + employee, +ORDER BY LastName DESC; +|"DepartmentID", "LastName", "", "", "", "" +[ Williams Will ] +[34 Smith Smit ] +[34 Robinson Robi ] +[31 Rafferty Raff ] +[33 Jones Jone ] +[33 Heisenberg Heis ] + +-- S 193 +SELECT + DepartmentID AS x, + DepartmentID<<1 AS a, + 1<>1 AS a, + uint(1)<<63>>uint(DepartmentID) AS b, +FROM + employee, +WHERE DepartmentID IS NOT NULL +ORDER BY x; +|"x", "a", "b" +[31 15 4294967296] +[33 16 1073741824] +[33 16 1073741824] +[34 17 536870912] +[34 17 536870912] + +-- S 195 +SELECT DISTINCT DepartmentID +FROM employee +WHERE DepartmentID IS NOT NULL; +|"DepartmentID" +[31] +[33] +[34] + +-- S 196 +SELECT DISTINCT e.DepartmentID, d.DepartmentID, e.LastName +FROM employee AS e, department AS d +WHERE e.DepartmentID == d.DepartmentID; +|"e.DepartmentID", "d.DepartmentID", "e.LastName" +[31 31 Rafferty] +[33 33 Heisenberg] +[33 33 Jones] +[34 34 Robinson] +[34 34 Smith] + +-- S 197 +SELECT DISTINCT e.DepartmentID, d.DepartmentID, e.LastName +FROM employee AS e, department AS d +WHERE e.DepartmentID == d.DepartmentID +ORDER BY e.LastName; +|"e.DepartmentID", "d.DepartmentID", "e.LastName" +[33 33 Heisenberg] +[33 33 Jones] +[31 31 Rafferty] +[34 34 Robinson] +[34 34 Smith] + +-- S 198, http://en.wikipedia.org/wiki/Join_(SQL)#Cross_join +SELECT * +FROM employee, department +ORDER BY employee.LastName, department.DepartmentID; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[Heisenberg 33 31 Sales] +[Heisenberg 33 33 Engineering] +[Heisenberg 33 34 Clerical] +[Heisenberg 33 35 Marketing] +[Jones 33 31 Sales] +[Jones 33 33 Engineering] +[Jones 33 34 Clerical] +[Jones 33 35 Marketing] +[Rafferty 31 31 Sales] +[Rafferty 31 33 Engineering] +[Rafferty 31 34 Clerical] +[Rafferty 31 35 Marketing] +[Robinson 34 31 Sales] +[Robinson 34 33 Engineering] +[Robinson 34 34 Clerical] +[Robinson 34 35 Marketing] +[Smith 34 31 Sales] +[Smith 34 33 Engineering] +[Smith 34 34 Clerical] +[Smith 34 35 Marketing] +[Williams 31 Sales] +[Williams 33 Engineering] +[Williams 34 Clerical] +[Williams 35 Marketing] + +-- S 199, http://en.wikipedia.org/wiki/Join_(SQL)#Inner_join +SELECT * +FROM employee, department +WHERE employee.DepartmentID == department.DepartmentID +ORDER BY employee.LastName, department.DepartmentID; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] + +-- S 200 +BEGIN TRANSACTION; + INSERT INTO department (DepartmentID, DepartmentName) + SELECT DepartmentID+1000, DepartmentName+"/headquarters" + FROM department; +COMMIT; +SELECT * FROM department +ORDER BY DepartmentID; +|"DepartmentID", "DepartmentName" +[31 Sales] +[33 Engineering] +[34 Clerical] +[35 Marketing] +[1031 Sales/headquarters] +[1033 Engineering/headquarters] +[1034 Clerical/headquarters] +[1035 Marketing/headquarters] + +-- S 201` +BEGIN TRANSACTION; + INSERT INTO department (DepartmentName, DepartmentID) + SELECT DepartmentName+"/headquarters", DepartmentID+1000 + FROM department; +COMMIT; +SELECT * FROM department +ORDER BY DepartmentID; +|"DepartmentID", "DepartmentName" +[31 Sales] +[33 Engineering] +[34 Clerical] +[35 Marketing] +[1031 Sales/headquarters] +[1033 Engineering/headquarters] +[1034 Clerical/headquarters] +[1035 Marketing/headquarters] + +-- S 202 +BEGIN TRANSACTION; + DELETE FROM department; +COMMIT; +SELECT * FROM department +|"DepartmentID", "DepartmentName" + +-- S 203 +BEGIN TRANSACTION; + DELETE FROM department + WHERE DepartmentID == 35 OR DepartmentName != "" && DepartmentName[0] == 'C'; +COMMIT; +SELECT * FROM department +ORDER BY DepartmentID; +|"DepartmentID", "DepartmentName" +[31 Sales] +[33 Engineering] + +-- S 204 +SELECT id(), LastName +FROM employee +ORDER BY id(); +|"", "LastName" +[5 Rafferty] +[6 Jones] +[7 Heisenberg] +[8 Robinson] +[9 Smith] +[10 Williams] + +-- S 205 //TODO investigate plan, add variant w/ DESC, check plan. +BEGIN TRANSACTION; + DELETE FROM employee + WHERE LastName == "Jones"; +COMMIT; +SELECT id(), LastName +FROM employee +ORDER BY id(); +|"", "LastName" +[5 Rafferty] +[7 Heisenberg] +[8 Robinson] +[9 Smith] +[10 Williams] + +-- S 206 +BEGIN TRANSACTION; + DELETE FROM employee + WHERE LastName == "Jones"; + INSERT INTO employee (LastName) VALUES ("Jones"); +COMMIT; +SELECT id(), LastName +FROM employee +ORDER BY id(); +|"", "LastName" +[5 Rafferty] +[7 Heisenberg] +[8 Robinson] +[9 Smith] +[10 Williams] +[11 Jones] + +-- S 207 +SELECT id(), e.LastName, e.DepartmentID, d.DepartmentID +FROM + employee AS e, + department AS d, +WHERE e.DepartmentID == d.DepartmentID +ORDER BY e.LastName; +|"", "e.LastName", "e.DepartmentID", "d.DepartmentID" +[ Heisenberg 33 33] +[ Jones 33 33] +[ Rafferty 31 31] +[ Robinson 34 34] +[ Smith 34 34] + +-- S 208 +SELECT e.ID, e.LastName, e.DepartmentID, d.DepartmentID +FROM + (SELECT id() AS ID, LastName, DepartmentID FROM employee;) AS e, + department AS d, +WHERE e.DepartmentID == d.DepartmentID +ORDER BY e.ID; +|"e.ID", "e.LastName", "e.DepartmentID", "d.DepartmentID" +[5 Rafferty 31 31] +[6 Jones 33 33] +[7 Heisenberg 33 33] +[8 Robinson 34 34] +[9 Smith 34 34] + +-- S 209 +BEGIN TRANSACTION; + UPDATE none + DepartmentID = DepartmentID+1000, + WHERE DepartmentID == 33; +COMMIT; +SELECT * FROM employee; +||table.*not.*exist + +-- S 210 +BEGIN TRANSACTION; + UPDATE employee + FirstName = "Williams" + WHERE DepartmentID == 33; +COMMIT; +SELECT * FROM employee; +||unknown.*FirstName + +-- S 211 +BEGIN TRANSACTION; + UPDATE employee + DepartmentID = DepartmentID+1000, + WHERE DepartmentID == 33; +COMMIT; +SELECT * FROM employee +ORDER BY LastName; +|"LastName", "DepartmentID" +[Heisenberg 1033] +[Jones 1033] +[Rafferty 31] +[Robinson 34] +[Smith 34] +[Williams ] + +-- S 212 +BEGIN TRANSACTION; + UPDATE employee + DepartmentID = DepartmentID+1000, + LastName = "Mr. "+LastName + WHERE id() == 7; +COMMIT; +SELECT * FROM employee +ORDER BY LastName DESC; +|"LastName", "DepartmentID" +[Williams ] +[Smith 34] +[Robinson 34] +[Rafferty 31] +[Mr. Heisenberg 1033] +[Jones 33] + +-- S 213 +BEGIN TRANSACTION; + UPDATE employee + LastName = "Mr. "+LastName, + DepartmentID = DepartmentID+1000, + WHERE id() == 7; +COMMIT; +SELECT * FROM employee +ORDER BY LastName DESC; +|"LastName", "DepartmentID" +[Williams ] +[Smith 34] +[Robinson 34] +[Rafferty 31] +[Mr. Heisenberg 1033] +[Jones 33] + +-- S 214 +BEGIN TRANSACTION; + UPDATE employee + DepartmentID = DepartmentID+1000; +COMMIT; +SELECT * FROM employee +ORDER BY LastName; +|"LastName", "DepartmentID" +[Heisenberg 1033] +[Jones 1033] +[Rafferty 1031] +[Robinson 1034] +[Smith 1034] +[Williams ] + +-- S 215 +BEGIN TRANSACTION; + UPDATE employee + DepartmentId = DepartmentID+1000; +COMMIT; +SELECT * FROM employee; +||unknown + +-- S 216 +BEGIN TRANSACTION; + UPDATE employee + DepartmentID = DepartmentId+1000; +COMMIT; +SELECT * FROM employee; +||unknown + +-- S 217 +BEGIN TRANSACTION; + UPDATE employee + DepartmentID = "foo"; +COMMIT; +SELECT * FROM employee; +||type string.*type int64 + +-- S 218 +SELECT foo[len()] FROM bar; +||missing argument + +-- S 219 +SELECT foo[len(42)] FROM bar; +||invalid argument + +-- S 220 +SELECT foo[len(42, 24)] FROM bar; +||too many + +-- S 221 +SELECT foo[len("baz")] FROM bar; +||table + +-- S 222 +SELECT LastName[len("baz")-4] FROM employee; +||invalid string index + +-- S 223 +SELECT LastName[:len(LastName)-3] AS y FROM employee +ORDER BY y; +|"y" +[Heisenb] +[Jo] +[Raffe] +[Robin] +[Sm] +[Willi] + +-- S 224 +SELECT complex(float32(DepartmentID+int(id())), 0) AS x, complex(DepartmentID+int(id()), 0) +FROM employee +ORDER by real(x) DESC; +|"x", "" +[(43+0i) (43+0i)] +[(42+0i) (42+0i)] +[(40+0i) (40+0i)] +[(39+0i) (39+0i)] +[(36+0i) (36+0i)] +[ ] + +-- S 225 +SELECT real(complex(float32(DepartmentID+int(id())), 0)) AS x, real(complex(DepartmentID+int(id()), 0)) +FROM employee +ORDER BY x DESC; +|"x", "" +[43 43] +[42 42] +[40 40] +[39 39] +[36 36] +[ ] + +-- S 226 +SELECT imag(complex(0, float32(DepartmentID+int(id())))) AS x, imag(complex(0, DepartmentID+int(id()))) +FROM employee +ORDER BY x DESC; +|"x", "" +[43 43] +[42 42] +[40 40] +[39 39] +[36 36] +[ ] + +-- 227 +BEGIN TRANSACTION; + CREATE TABLE t (c string); + INSERT INTO t VALUES("foo"), ("bar"); + DELETE FROM t WHERE c == "foo"; +COMMIT; +SELECT 100*id(), c FROM t; +|"", "c" +[200 bar] + +-- 228 +BEGIN TRANSACTION; + CREATE TABLE a (a int); + CREATE TABLE b (b int); + CREATE TABLE c (c int); + DROP TABLE a; +COMMIT; +SELECT * FROM a; +||table a does not exist + +-- 229 +BEGIN TRANSACTION; + CREATE TABLE a (a int); + CREATE TABLE b (b int); + CREATE TABLE c (c int); + DROP TABLE a; +COMMIT; +SELECT * FROM b; +|"b" + +-- 230 +BEGIN TRANSACTION; + CREATE TABLE a (a int); + CREATE TABLE b (b int); + CREATE TABLE c (c int); + DROP TABLE a; +COMMIT; +SELECT * FROM c; +|"c" + +-- 231 +BEGIN TRANSACTION; + CREATE TABLE a (a int); + CREATE TABLE b (b int); + CREATE TABLE c (c int); + DROP TABLE b; +COMMIT; +SELECT * FROM a; +|"a" + +-- 232 +BEGIN TRANSACTION; + CREATE TABLE a (a int); + CREATE TABLE b (b int); + CREATE TABLE c (c int); + DROP TABLE b; +COMMIT; +SELECT * FROM b; +||table b does not exist + +-- 233 +BEGIN TRANSACTION; + CREATE TABLE a (a int); + CREATE TABLE b (b int); + CREATE TABLE c (c int); + DROP TABLE b; +COMMIT; +SELECT * FROM c; +|"c" + +-- 234 +BEGIN TRANSACTION; + CREATE TABLE a (a int); + CREATE TABLE b (b int); + CREATE TABLE c (c int); + DROP TABLE c; +COMMIT; +SELECT * FROM a; +|"a" + +-- 235 +BEGIN TRANSACTION; + CREATE TABLE a (a int); + CREATE TABLE b (b int); + CREATE TABLE c (c int); + DROP TABLE c; +COMMIT; +SELECT * FROM b; +|"b" + +-- 236 +BEGIN TRANSACTION; + CREATE TABLE a (a int); + CREATE TABLE b (b int); + CREATE TABLE c (c int); + DROP TABLE c; +COMMIT; +SELECT * FROM c; +||table c does not exist + +-- 237 +BEGIN TRANSACTION; + CREATE TABLE a (c int); + INSERT INTO a VALUES (10), (11), (12); + CREATE TABLE b (d int); + INSERT INTO b VALUES (20), (21), (22), (23); +COMMIT; +SELECT * FROM a, b; +|"a.c", "b.d" +[12 23] +[12 22] +[12 21] +[12 20] +[11 23] +[11 22] +[11 21] +[11 20] +[10 23] +[10 22] +[10 21] +[10 20] + +-- 238 +BEGIN TRANSACTION; + CREATE TABLE a (c int); + INSERT INTO a VALUES (0), (1), (2); +COMMIT; +SELECT + 9*x2.c AS x2, + 3*x1.c AS x1, + 1*x0.c AS x0, + 9*x2.c + 3*x1.c + x0.c AS y, +FROM + a AS x2, + a AS x1, + a AS x0, +ORDER BY y; +|"x2", "x1", "x0", "y" +[0 0 0 0] +[0 0 1 1] +[0 0 2 2] +[0 3 0 3] +[0 3 1 4] +[0 3 2 5] +[0 6 0 6] +[0 6 1 7] +[0 6 2 8] +[9 0 0 9] +[9 0 1 10] +[9 0 2 11] +[9 3 0 12] +[9 3 1 13] +[9 3 2 14] +[9 6 0 15] +[9 6 1 16] +[9 6 2 17] +[18 0 0 18] +[18 0 1 19] +[18 0 2 20] +[18 3 0 21] +[18 3 1 22] +[18 3 2 23] +[18 6 0 24] +[18 6 1 25] +[18 6 2 26] + +-- 239 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + INSERT INTO t VALUES (242); + DELETE FROM t WHERE c != 0; +COMMIT; +SELECT * FROM t +|"c" + +-- 240 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (242), (12, 24); +COMMIT; +||expect + +-- 241 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (24, 2), (1224); +COMMIT; +||expect + +-- 242 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +ROLLBACK; +SELECT * from t; +||does not exist + +-- 243 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +BEGIN TRANSACTION; + DROP TABLE T; +COMMIT; +SELECT * from t; +||does not exist + +-- 244 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +BEGIN TRANSACTION; + DROP TABLE t; +ROLLBACK; +SELECT * from t; +|"i" + +-- 245 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (int(1.2)); +COMMIT; +SELECT * FROM t; +||truncated + +-- 246 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (string(65.0)); +COMMIT; +SELECT * FROM t; +||cannot convert + +-- 247 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES (string(65)); +COMMIT; +SELECT * FROM t; +|"s" +[A] + +-- 248 +BEGIN TRANSACTION; + CREATE TABLE t (i uint32); + INSERT INTO t VALUES (uint32(int8(uint16(0x10F0)))); +COMMIT; +SELECT i == 0xFFFFFFF0 FROM t; +|"" +[true] + +-- 249 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + (string('a')), // "a" + (string(-1)), // "\ufffd" == "\xef\xbf\xbd" + (string(0xf8)), // "\u00f8" == "ø" == "\xc3\xb8" + (string(0x65e5)), // "\u65e5" == "日" == "\xe6\x97\xa5" + ; +COMMIT; +SELECT + id() == 1 && s == "a" OR + id() == 2 && s == "\ufffd" && s == "\xef\xbf\xbd" OR + id() == 3 && s == "\u00f8" && s == "ø" && s == "\xc3\xb8" OR + id() == 4 && s == "\u65e5" && s == "日" && s == "\xe6\x97\xa5" +FROM t; +|"" +[true] +[true] +[true] +[true] + +-- 250 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (0); +COMMIT; +SELECT 2.3+1, 1+2.3 FROM t; +|"", "" +[3.3 3.3] + +-- 251 +BEGIN TRANSACTION; + CREATE TABLE t (i byte); + INSERT INTO t VALUES (-1+byte(2)); +COMMIT; +SELECT * FROM t; +||mismatched + +-- 252 +BEGIN TRANSACTION; + CREATE TABLE t (i byte); + INSERT INTO t VALUES (1+byte(2)); +COMMIT; +SELECT * FROM t; +|"i" +[3] + +-- 253 +BEGIN TRANSACTION; + CREATE TABLE t (i byte); + INSERT INTO t VALUES (255+byte(2)); +COMMIT; +SELECT * FROM t; +|"i" +[1] + +-- 254 +BEGIN TRANSACTION; + CREATE TABLE t (i byte); + INSERT INTO t VALUES (256+byte(2)); +COMMIT; +SELECT * FROM t; +||mismatched + +-- 255 +BEGIN TRANSACTION; + CREATE TABLE t (i int8); + INSERT INTO t VALUES (127+int8(2)); +COMMIT; +SELECT * FROM t; +|"i" +[-127] + +-- 256 +BEGIN TRANSACTION; + CREATE TABLE t (i int8); + INSERT INTO t VALUES (-129+int8(2)); +COMMIT; +SELECT * FROM t; +||mismatched + +-- 257 +BEGIN TRANSACTION; + CREATE TABLE t (i int8); + INSERT INTO t VALUES (-128+int8(2)); +COMMIT; +SELECT * FROM t; +|"i" +[-126] + +-- 258 +BEGIN TRANSACTION; + CREATE TABLE t (i int8); + INSERT INTO t VALUES (128+int8(2)); +COMMIT; +SELECT * FROM t; +||mismatched + +-- S 259 +SELECT count(none) FROM employee; +||unknown + +-- S 260 +SELECT count() FROM employee; +|"" +[6] + +-- S 261 +SELECT count() AS y FROM employee; +|"y" +[6] + +-- S 262 +SELECT 3*count() AS y FROM employee; +|"y" +[18] + +-- S 263 +SELECT count(LastName) FROM employee; +|"" +[6] + +-- S 264 +SELECT count(DepartmentID) FROM employee; +|"" +[5] + +-- S 265 +SELECT count() - count(DepartmentID) FROM employee; +|"" +[1] + +-- S 266 +SELECT min(LastName), min(DepartmentID) FROM employee; +|"", "" +[Heisenberg 31] + +-- S 267 +SELECT max(LastName), max(DepartmentID) FROM employee; +|"", "" +[Williams 34] + +-- S 268 +SELECT sum(LastName), sum(DepartmentID) FROM employee; +||cannot + +-- S 269 +SELECT sum(DepartmentID) FROM employee; +|"" +[165] + +-- S 270 +SELECT avg(DepartmentID) FROM employee; +|"" +[33] + +-- S 271 +SELECT DepartmentID FROM employee GROUP BY none; +||unknown + +-- S 272 +SELECT DepartmentID, sum(DepartmentID) AS s FROM employee GROUP BY DepartmentID ORDER BY s DESC; +|"DepartmentID", "s" +[34 68] +[33 66] +[31 31] +[ ] + +-- S 273 +SELECT DepartmentID, count(LastName+string(DepartmentID)) AS y FROM employee GROUP BY DepartmentID ORDER BY y DESC ; +|"DepartmentID", "y" +[34 2] +[33 2] +[31 1] +[ 0] + +-- S 274 +SELECT DepartmentID, sum(2*DepartmentID) AS s FROM employee GROUP BY DepartmentID ORDER BY s DESC; +|"DepartmentID", "s" +[34 136] +[33 132] +[31 62] +[ ] + +-- S 275 +SELECT min(2*DepartmentID) FROM employee; +|"" +[62] + +-- S 276 +SELECT max(2*DepartmentID) FROM employee; +|"" +[68] + +-- S 277 +SELECT avg(2*DepartmentID) FROM employee; +|"" +[66] + +-- S 278 +SELECT * FROM employee GROUP BY DepartmentID; +|"LastName", "DepartmentID" +[Williams ] +[Rafferty 31] +[Heisenberg 33] +[Smith 34] + +-- S 279 +SELECT * FROM employee GROUP BY DepartmentID ORDER BY LastName DESC; +|"LastName", "DepartmentID" +[Williams ] +[Smith 34] +[Rafferty 31] +[Heisenberg 33] + +-- S 280 +SELECT * FROM employee GROUP BY DepartmentID, LastName ORDER BY LastName DESC; +|"LastName", "DepartmentID" +[Williams ] +[Smith 34] +[Robinson 34] +[Rafferty 31] +[Jones 33] +[Heisenberg 33] + +-- S 281 +SELECT * FROM employee GROUP BY LastName, DepartmentID ORDER BY LastName DESC; +|"LastName", "DepartmentID" +[Williams ] +[Smith 34] +[Robinson 34] +[Rafferty 31] +[Jones 33] +[Heisenberg 33] + +-- 282 +BEGIN TRANSACTION; + CREATE TABLE s (i int); + CREATE TABLE t (i int); +COMMIT; +BEGIN TRANSACTION; + DROP TABLE s; +COMMIT; +SELECT * FROM t; +|"i" + +-- 283 +BEGIN TRANSACTION; + CREATE TABLE t (n int); +COMMIT; +SELECT count() FROM t; +|"" +[0] + +-- 284 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT count() FROM t; +|"" +[2] + +-- 285 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT count() FROM t WHERE n < 2; +|"" +[2] + +-- 286 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT count() FROM t WHERE n < 1; +|"" +[1] + +-- 287 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT count() FROM t WHERE n < 0; +|"" +[0] + +-- 288 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT s+10 FROM (SELECT sum(n) AS s FROM t WHERE n < 2); +|"" +[11] + +-- 289 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT s+10 FROM (SELECT sum(n) AS s FROM t WHERE n < 1); +|"" +[10] + +-- 290 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT s+10 FROM (SELECT sum(n) AS s FROM t WHERE n < 0); +|"" +[] + +-- 291 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT sum(n) AS s FROM t WHERE n < 2; +|"s" +[1] + +-- 292 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT sum(n) AS s FROM t WHERE n < 1; +|"s" +[0] + +-- 293 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1); +COMMIT; +SELECT sum(n) AS s FROM t WHERE n < 0; +|"s" +[] + +-- 294 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t SELECT count() FROM t; + INSERT INTO t SELECT count() FROM t; + INSERT INTO t SELECT count() FROM t; +COMMIT; +SELECT count() FROM t; +|"" +[3] + +-- 295 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t SELECT count() FROM t; + INSERT INTO t SELECT count() FROM t; + INSERT INTO t SELECT count() FROM t; + INSERT INTO t SELECT * FROM t; +COMMIT; +SELECT count() FROM t; +|"" +[6] + +-- 296 +BEGIN TRANSACTION; + CREATE TABLE t (n int); + INSERT INTO t VALUES (0), (1), (2); + INSERT INTO t SELECT * FROM t; +COMMIT; +SELECT count() FROM t; +|"" +[6] + +-- 297 +BEGIN TRANSACTION; + CREATE TABLE t(S string); + INSERT INTO t SELECT "perfect!" FROM (SELECT count() AS cnt FROM t WHERE S == "perfect!") WHERE cnt == 0; +COMMIT; +SELECT count() FROM t; +|"" +[1] + +-- 298 +BEGIN TRANSACTION; + CREATE TABLE t(S string); + INSERT INTO t SELECT "perfect!" FROM (SELECT count() AS cnt FROM t WHERE S == "perfect!") WHERE cnt == 0; + INSERT INTO t SELECT "perfect!" FROM (SELECT count() AS cnt FROM t WHERE S == "perfect!") WHERE cnt == 0; +COMMIT; +SELECT count() FROM t; +|"" +[1] + +-- 299 +BEGIN TRANSACTION; + CREATE TABLE t(c blob); + INSERT INTO t VALUES (blob("a")); +COMMIT; +SELECT * FROM t; +|"c" +[[97]] + +-- 300 +BEGIN TRANSACTION; + CREATE TABLE t(c blob); + INSERT INTO t VALUES (blob(` +0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +`)); +COMMIT; +SELECT * FROM t; +|"c" +[[10 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 10]] + +-- 301 +BEGIN TRANSACTION; + CREATE TABLE t(c blob); + INSERT INTO t VALUES (blob( +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF")); +COMMIT; +SELECT * FROM t; +|"c" +[[48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 65 66 67 68 69 70]] + +-- 302 +BEGIN TRANSACTION; + CREATE TABLE t(c blob); + INSERT INTO t VALUES (blob( +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF"+ +"!")); +COMMIT; +SELECT * FROM t; +|"c" +[[48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 65 66 67 68 69 70 33]] + +-- 303 +BEGIN TRANSACTION; + CREATE TABLE t(c blob); + INSERT INTO t VALUES (blob("hell\xc3\xb8")); +COMMIT; +SELECT string(c) FROM t; +|"" +[hellø] + +-- 304 +BEGIN TRANSACTION; + CREATE TABLE t(c blob); + INSERT INTO t VALUES (blob( +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ +"0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF"+ +"!")); +COMMIT; +SELECT string(c) FROM t; +|"" +[0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!] + +-- 305 +BEGIN TRANSACTION; + CREATE TABLE t(c blob); + INSERT INTO t VALUES (blob("")); +COMMIT; +SELECT string(c) FROM t; +|"" +[] + +-- 306 +BEGIN TRANSACTION; + CREATE TABLE t(c blob); + INSERT INTO t VALUES (blob("hellø")); +COMMIT; +SELECT * FROM t; +|"c" +[[104 101 108 108 195 184]] + +-- 307 +BEGIN TRANSACTION; + CREATE TABLE t(c blob); + INSERT INTO t VALUES (blob("")); +COMMIT; +SELECT * FROM t; +|"c" +[[]] + +-- 308 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob("0")), + ; +COMMIT; +SELECT * FROM t; +|"i", "b" +[0 [48]] + +-- 309 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob("0")), + (1, blob("1")), + ; +COMMIT; +SELECT * FROM t; +|"i", "b" +[1 [49]] +[0 [48]] + +-- 310 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob("0")), + (1, blob("1")), + (2, blob("2")), + ; +COMMIT; +SELECT * FROM t; +|"i", "b" +[2 [50]] +[1 [49]] +[0 [48]] + +-- 311 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob("0")), + ; + DELETE FROM t WHERE i == 0; +COMMIT; +SELECT * FROM t; +|"i", "b" + +-- 312 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob("0")), + (1, blob("1")), + ; + DELETE FROM t WHERE i == 0; +COMMIT; +SELECT * FROM t; +|"i", "b" +[1 [49]] + +-- 313 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob("0")), + (1, blob("1")), + ; + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT * FROM t; +|"i", "b" +[0 [48]] + +-- 314 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob("0")), + (1, blob("1")), + (2, blob("2")), + ; + DELETE FROM t WHERE i == 0; +COMMIT; +SELECT * FROM t; +|"i", "b" +[2 [50]] +[1 [49]] + +-- 315 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob("0")), + (1, blob("1")), + (2, blob("2")), + ; + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT * FROM t; +|"i", "b" +[2 [50]] +[0 [48]] + +-- 316 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob("0")), + (1, blob("1")), + (2, blob("2")), + ; + DELETE FROM t WHERE i == 2; +COMMIT; +SELECT * FROM t; +|"i", "b" +[1 [49]] +[0 [48]] + +-- 317 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + ; + DELETE FROM t WHERE i == 0; +COMMIT; +SELECT i, string(b) FROM t; +|"i", "" + +-- 318 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + (1, blob( + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + ; + DELETE FROM t WHERE i == 0; +COMMIT; +SELECT i, string(b) FROM t; +|"i", "" +[1 1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!] + +-- 319 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + (1, blob( + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + ; + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT i, string(b) FROM t; +|"i", "" +[0 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!] + +-- 320 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + (1, blob( + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + (2, blob( + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + ; + DELETE FROM t WHERE i == 0; +COMMIT; +SELECT i, string(b) FROM t; +|"i", "" +[2 2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef2123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!] +[1 1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!] + +-- 321 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + (1, blob( + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + (2, blob( + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + ; + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT i, string(b) FROM t; +|"i", "" +[2 2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef2123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!] +[0 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!] + +-- 322 +BEGIN TRANSACTION; + CREATE TABLE t(i int, b blob); + INSERT INTO t VALUES + (0, blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + (1, blob( + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + (2, blob( + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!")), + ; + DELETE FROM t WHERE i == 2; +COMMIT; +SELECT i, string(b) FROM t; +|"i", "" +[1 1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!] +[0 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!] + +-- 323 +BEGIN TRANSACTION; + CREATE TABLE t (c bool); + INSERT INTO t VALUES (false), (true); +COMMIT; +SELECT * FROM t ORDER BY true, c, false; +||cannot .* bool + +-- 324 +BEGIN TRANSACTION; + CREATE TABLE t (c bool, i int); + INSERT INTO t VALUES (false, 1), (true, 2), (false, 10), (true, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[false 11] +[true 22] + +-- 325 +BEGIN TRANSACTION; + CREATE TABLE t (c int8); + INSERT INTO t VALUES (1), (2); +COMMIT; +SELECT * FROM t ORDER BY 42, c, 24; +|"c" +[1] +[2] + +-- 326 +BEGIN TRANSACTION; + CREATE TABLE t (c int8, i int); + INSERT INTO t VALUES (99, 1), (100, 2), (99, 10), (100, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[99 11] +[100 22] + +-- 327 +BEGIN TRANSACTION; + CREATE TABLE t (c blob); + INSERT INTO t VALUES (blob("A")), (blob("B")); +COMMIT; +SELECT * FROM t ORDER BY 42, c, 24; +||cannot .* \[\]uint8 + +-- 328 +BEGIN TRANSACTION; + CREATE TABLE t (c blob, i int); + INSERT INTO t VALUES (blob("A"), 1), (blob("B"), 2); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[[65] 1] +[[66] 2] + +-- 329 +BEGIN TRANSACTION; + CREATE TABLE t (c blob, i int); + INSERT INTO t VALUES (blob("A"), 10), (blob("B"), 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[[65] 10] +[[66] 20] + +-- 330 +BEGIN TRANSACTION; + CREATE TABLE t (c blob, i int); + INSERT INTO t VALUES (blob("A"), 1), (blob("B"), 2), (blob("A"), 10), (blob("B"), 20); +COMMIT; +SELECT * FROM t; +|"c", "i" +[[66] 20] +[[65] 10] +[[66] 2] +[[65] 1] + +-- 331 +BEGIN TRANSACTION; + CREATE TABLE t (c string, i int); + INSERT INTO t VALUES ("A", 1), ("B", 2), ("A", 10), ("B", 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[A 11] +[B 22] + +-- 332 +BEGIN TRANSACTION; + CREATE TABLE t (c blob, i int); + INSERT INTO t VALUES (blob("A"), 1), (blob("B"), 2), (blob("A"), 10), (blob("B"), 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[[65] 11] +[[66] 22] + +-- 333 +BEGIN TRANSACTION; + CREATE TABLE t (c byte); + INSERT INTO t VALUES (42), (314); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +||overflow + +-- 334 +BEGIN TRANSACTION; + CREATE TABLE t (c byte); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 335 +BEGIN TRANSACTION; + CREATE TABLE t (c byte, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 336 +BEGIN TRANSACTION; + CREATE TABLE t (c byte); + INSERT INTO t VALUES (42), (3.14); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +||truncated + +-- 337 +BEGIN TRANSACTION; + CREATE TABLE t (c complex64); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +||cannot order by + +-- 338 +BEGIN TRANSACTION; + CREATE TABLE t (c complex64, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[(100+0i) 11] +[(101+0i) 22] + +-- 339 +BEGIN TRANSACTION; + CREATE TABLE t (c complex128); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +||cannot order by + +-- 340 +BEGIN TRANSACTION; + CREATE TABLE t (c complex128, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[(100+0i) 11] +[(101+0i) 22] + +-- 341 +BEGIN TRANSACTION; + CREATE TABLE t (c float); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 342 +BEGIN TRANSACTION; + CREATE TABLE t (c float, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 343 +BEGIN TRANSACTION; + CREATE TABLE t (c float64); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 344 +BEGIN TRANSACTION; + CREATE TABLE t (c float64, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 345 +BEGIN TRANSACTION; + CREATE TABLE t (c float32); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 346 +BEGIN TRANSACTION; + CREATE TABLE t (c float32, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 347 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 348 +BEGIN TRANSACTION; + CREATE TABLE t (c int, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 349 +BEGIN TRANSACTION; + CREATE TABLE t (c int64); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 350 +BEGIN TRANSACTION; + CREATE TABLE t (c int64, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 351 +BEGIN TRANSACTION; + CREATE TABLE t (c int8); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 352 +BEGIN TRANSACTION; + CREATE TABLE t (c int8, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 353 +BEGIN TRANSACTION; + CREATE TABLE t (c int16); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 354 +BEGIN TRANSACTION; + CREATE TABLE t (c int16, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 355 +BEGIN TRANSACTION; + CREATE TABLE t (c int32); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 356 +BEGIN TRANSACTION; + CREATE TABLE t (c int32, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 357 +BEGIN TRANSACTION; + CREATE TABLE t (c uint); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 358 +BEGIN TRANSACTION; + CREATE TABLE t (c uint, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 359 +BEGIN TRANSACTION; + CREATE TABLE t (c uint64); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 360 +BEGIN TRANSACTION; + CREATE TABLE t (c uint64, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 361 +BEGIN TRANSACTION; + CREATE TABLE t (c uint8); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 362 +BEGIN TRANSACTION; + CREATE TABLE t (c uint8, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 363 +BEGIN TRANSACTION; + CREATE TABLE t (c uint16); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 364 +BEGIN TRANSACTION; + CREATE TABLE t (c uint16, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 365 +BEGIN TRANSACTION; + CREATE TABLE t (c uint32); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 366 +BEGIN TRANSACTION; + CREATE TABLE t (c uint32, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 367 +BEGIN TRANSACTION; + CREATE TABLE t (c blob, i int); + INSERT INTO t VALUES (blob("A"), 1), (blob("B"), 2); + UPDATE t c = blob("C") WHERE i == 2; +COMMIT; +SELECT * FROM t; +|"c", "i" +[[67] 2] +[[65] 1] + +-- 368 +BEGIN TRANSACTION; + CREATE TABLE t (c blob, i int); + INSERT INTO t VALUES + (blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!" + ), 1), + (blob( + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!" + ), 2); + UPDATE t c = blob( + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "2123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!" + ) WHERE i == 2; +COMMIT; +SELECT * FROM t; +|"c", "i" +[[50 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 50 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 50 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 50 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 65 66 67 68 69 70 33] 2] +[[48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 65 66 67 68 69 70 33] 1] + +-- 369 +BEGIN TRANSACTION; + CREATE TABLE t (c blob, i int); + INSERT INTO t VALUES + (blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!" + ), 1), + (blob( + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "1123456789abcdef0123456789abcdef0123456789abcdef0123456789ABCDEF!" + ), 2); + UPDATE t i = 42 WHERE i == 2; +COMMIT; +SELECT * FROM t; +|"c", "i" +[[49 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 49 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 49 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 49 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 65 66 67 68 69 70 33] 42] +[[48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 97 98 99 100 101 102 48 49 50 51 52 53 54 55 56 57 65 66 67 68 69 70 33] 1] + +-- 370 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * FROM t; +|"i" +[1] + +-- 371 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + INSERT INTO t VALUES (bigint("1")); +COMMIT; +SELECT * FROM t; +|"i" +[1] + +-- 372 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + INSERT INTO t VALUES (bigint("12345678901234567890123456789")); +COMMIT; +SELECT * FROM t; +|"i" +[12345678901234567890123456789] + +-- 373 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + INSERT INTO t VALUES (bigint(2e10)); +COMMIT; +SELECT * FROM t; +|"i" +[20000000000] + +-- 374 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + INSERT INTO t VALUES (bigint(2e18)); +COMMIT; +SELECT * FROM t; +|"i" +[2000000000000000000] + +-- 375 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + INSERT INTO t VALUES (bigint(2e19)); +COMMIT; +SELECT * FROM t; +|"i" +[20000000000000000000] + +-- 376 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + INSERT INTO t VALUES (bigint(2e20)); +COMMIT; +SELECT * FROM t; +|"i" +[200000000000000000000] + +-- 377 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + INSERT INTO t VALUES (bigint("0x1fffffffffffffff")); +COMMIT; +SELECT * FROM t; +|"i" +[2305843009213693951] + +-- 378 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + INSERT INTO t VALUES (bigint("0x1ffffffffffffffffffffff")); +COMMIT; +SELECT * FROM t; +|"i" +[618970019642690137449562111] + +-- 379 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42] +[114] + +-- 380 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100 11] +[101 22] + +-- 381 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT * FROM t WHERE c > 100 ORDER BY c DESC; +|"c" +[111] +[110] +[101] + +-- 382 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT * FROM t WHERE c < 110 ORDER BY c; +|"c" +[100] +[101] + +-- 383 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT * FROM t WHERE c <= 110 ORDER BY c; +|"c" +[100] +[101] +[110] + +-- 384 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT * FROM t WHERE c >= 110 ORDER BY c; +|"c" +[110] +[111] + +-- 385 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT * FROM t WHERE c != 110 ORDER BY c; +|"c" +[100] +[101] +[111] + +-- 386 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT * FROM t WHERE c == 110 ORDER BY c; +|"c" +[110] + +-- 387 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT (c+1000) as s FROM t ORDER BY s; +|"s" +[1100] +[1101] +[1110] +[1111] + +-- 388 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT (1000-c) as s FROM t ORDER BY s; +|"s" +[889] +[890] +[899] +[900] + +-- 389 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT (c>>1) as s FROM t ORDER BY s; +|"s" +[50] +[50] +[55] +[55] + +-- 390 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (100), (101), (110), (111); +COMMIT; +SELECT (c<<1) as s FROM t ORDER BY s; +|"s" +[200] +[202] +[220] +[222] + +-- 391 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (0x137f0); +COMMIT; +SELECT * FROM t WHERE c&0x55555 == 0x11550; +|"c" +[79856] + +-- 392 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (0x137f0); +COMMIT; +SELECT * FROM t WHERE c∨0x55555 == 0x577f5; +|"c" +[79856] + +-- 393 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (0x137f0); +COMMIT; +SELECT * FROM t WHERE c&^0x55555 == 0x022a0; +|"c" +[79856] + +-- 394 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (0x137f0); +COMMIT; +SELECT * FROM t WHERE c^0x55555 == 0x462a5; +|"c" +[79856] + +-- 395 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (0x137f0); +COMMIT; +SELECT * FROM t WHERE c%256 == 0xf0; +|"c" +[79856] + +-- 396 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (0x137f0); +COMMIT; +SELECT * FROM t WHERE c*16 == 0x137f00; +|"c" +[79856] + +-- 397 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (0x137f0); +COMMIT; +SELECT * FROM t WHERE ^c == -(0x137f0+1); +|"c" +[79856] + +-- 398 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (0x137f0); +COMMIT; +SELECT * FROM t WHERE +c == 0x137f0; +|"c" +[79856] + +-- 399 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint); + INSERT INTO t VALUES (0x137f0); +COMMIT; +SELECT * FROM t WHERE -c == -79856; +|"c" +[79856] + +-- 400 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat); + INSERT INTO t VALUES (42), (114); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[42/1] +[114/1] + +-- 401 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, i int); + INSERT INTO t VALUES (100, 1), (101, 2), (100, 10), (101, 20); +COMMIT; +SELECT c, sum(i) FROM t GROUP BY c; +|"c", "" +[100/1 11] +[101/1 22] + +-- 402 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat); + INSERT INTO t VALUES (42.24), (114e3); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[5944751508129055/140737488355328] +[114000/1] + +-- 403 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat); + INSERT INTO t VALUES ('A'), ('B'); +COMMIT; +SELECT * FROM t ORDER BY 15, c, 16; +|"c" +[65/1] +[66/1] + +-- 404 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat); + INSERT INTO t VALUES (bigrat("2/3")+bigrat("5/7")); +COMMIT; +SELECT * FROM t; +|"c" +[29/21] + +-- 405 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat); + INSERT INTO t VALUES (bigrat("2/3")-bigrat("5/7")); +COMMIT; +SELECT * FROM t; +|"c" +[-1/21] + +-- 406 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat); + INSERT INTO t VALUES (bigrat("2/3")*bigrat("5/7")); +COMMIT; +SELECT * FROM t; +|"c" +[10/21] + +-- 407 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint, d bigint); + INSERT INTO t VALUES (1, 0); +COMMIT; +SELECT c/d FROM t; +||division .* zero + +-- 408 +BEGIN TRANSACTION; + CREATE TABLE t (c bigint, d bigint); + INSERT INTO t VALUES (1, 0); +COMMIT; +SELECT c%d FROM t; +||division .* zero + +-- 409 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("5/7")); +COMMIT; +SELECT c == d FROM t; +|"" +[false] + +-- 410 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("4/6")); +COMMIT; +SELECT c == d FROM t; +|"" +[true] + +-- 411 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("5/7")); +COMMIT; +SELECT c != d FROM t; +|"" +[true] + +-- 412 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("4/6")); +COMMIT; +SELECT c != d FROM t; +|"" +[false] + +-- 413 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("5/7")); +COMMIT; +SELECT c < d FROM t; +|"" +[true] + +-- 414 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("4/6")); +COMMIT; +SELECT c < d FROM t; +|"" +[false] + +-- 415 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("5/7")); +COMMIT; +SELECT c <= d FROM t; +|"" +[true] + +-- 416 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("4/6")); +COMMIT; +SELECT c <= d FROM t; +|"" +[true] + +-- 417 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("5/7")); +COMMIT; +SELECT c > d FROM t; +|"" +[false] + +-- 418 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("4/6")); +COMMIT; +SELECT c > d FROM t; +|"" +[false] + +-- 419 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("5/7")); +COMMIT; +SELECT c >= d FROM t; +|"" +[false] + +-- 420 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("4/6")); +COMMIT; +SELECT c >= d FROM t; +|"" +[true] + +-- 421 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("5/7")); +COMMIT; +SELECT c / d FROM t; +|"" +[14/15] + +-- 422 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("0")); +COMMIT; +SELECT c / d FROM t; +||division .* zero + +-- 423 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("0")); +COMMIT; +SELECT c / (6-2*3) FROM t; +||division .* zero + +-- 424 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("5/7")); +COMMIT; +SELECT +c, -d FROM t; +|"", "" +[2/3 -5/7] + +-- 425 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat, d bigrat); + INSERT INTO t VALUES (bigrat("2/3"), bigrat("5/7")); +COMMIT; +SELECT 1+c, d+1, 1.5+c, d+1.5 FROM t; +|"", "", "", "" +[5/3 12/7 13/6 31/14] + +-- 426 +BEGIN TRANSACTION; + CREATE TABLE t (c bigrat); + INSERT INTO t VALUES (bigrat("355/113")); +COMMIT; +SELECT float(c) FROM t; +|"" +[3.1415929203539825] + +-- 427 +BEGIN TRANSACTION; + CREATE TABLE t (c time); + INSERT INTO t VALUES (date(2006, 1, 2, 15, 4, 5, 999999999, "CET")); +COMMIT; +SELECT formatTime(timeIn(c, "UTC"), "2006-01-02 15:04:05.999999999 -0700") FROM t; +|"" +[2006-01-02 14:04:05.999999999 +0000] + +-- 428 +BEGIN TRANSACTION; + CREATE TABLE t (c duration); + INSERT INTO t VALUES (duration("1s")), (duration("1m")), (duration("1h")); +COMMIT; +SELECT c, string(c) FROM t ORDER BY c; +|"c", "" +[1s 1s] +[1m0s 1m0s] +[1h0m0s 1h0m0s] + +-- 429 +BEGIN TRANSACTION; + CREATE TABLE t (c time); + INSERT INTO t VALUES (date(2013, 11, 26, 10, 18, 5, 999999999, "CET")); +COMMIT; +SELECT since(c) > duration("24h") FROM t; +|"" +[true] + +-- 430 +BEGIN TRANSACTION; + CREATE TABLE t (c time); + INSERT INTO t VALUES (date(2013, 11, 26, 10, 32, 5, 999999999, "CET")); +COMMIT; +SELECT !(since(c) < duration("24h")) FROM t; +|"" +[true] + +-- 431 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("42h21m11.999999994s"), + duration("42h21m11.999999995s"), + duration("42h21m11.999999996s"), + ), + ; +COMMIT; +SELECT a > a, a > b, a > c, b > a, b > b, b > c, c > a, c > b, c > c FROM t; +|"", "", "", "", "", "", "", "", "" +[false false false true false false true true false] + +-- 432 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("42h21m11.999999994s"), + duration("42h21m11.999999995s"), + duration("42h21m11.999999996s"), + ), + ; +COMMIT; +SELECT a < a, a < b, a < c, b < a, b < b, b < c, c < a, c < b, c < c FROM t; +|"", "", "", "", "", "", "", "", "" +[false true true false false true false false false] + +-- 433 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("42h21m11.999999994s"), + duration("42h21m11.999999995s"), + duration("42h21m11.999999996s"), + ), + ; +COMMIT; +SELECT a <= a, a <= b, a <= c, b <= a, b <= b, b <= c, c <= a, c <= b, c <= c FROM t; +|"", "", "", "", "", "", "", "", "" +[true true true false true true false false true] + +-- 434 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("42h21m11.999999994s"), + duration("42h21m11.999999995s"), + duration("42h21m11.999999996s"), + ), + ; +COMMIT; +SELECT a >= a, a >= b, a >= c, b >= a, b >= b, b >= c, c >= a, c >= b, c >= c FROM t; +|"", "", "", "", "", "", "", "", "" +[true false false true true false true true true] + +-- 435 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("42h21m11.999999994s"), + duration("42h21m11.999999995s"), + duration("42h21m11.999999996s"), + ), + ; +COMMIT; +SELECT a != a, a != b, a != c, b != a, b != b, b != c, c != a, c != b, c != c FROM t; +|"", "", "", "", "", "", "", "", "" +[false true true true false true true true false] + +-- 436 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("42h21m11.999999994s"), + duration("42h21m11.999999995s"), + duration("42h21m11.999999996s"), + ), + ; +COMMIT; +SELECT a == a, a == b, a == c, b == a, b == b, b == c, c == a, c == b, c == c FROM t; +|"", "", "", "", "", "", "", "", "" +[true false false false true false false false true] + +-- 437 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("5h"), + duration("3m"), + duration("2s"), + ), + ; +COMMIT; +SELECT b+c, a+c, a+b, a+b+c FROM t; +|"", "", "", "" +[3m2s 5h0m2s 5h3m0s 5h3m2s] + +-- 438 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("5h"), + duration("3m"), + duration("2s"), + ), + ; +COMMIT; +SELECT b-c, a-c, a-b, a-b-c FROM t; +|"", "", "", "" +[2m58s 4h59m58s 4h57m0s 4h56m58s] + +-- 439 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("5h"), + duration("3m"), + duration("2s"), + ), + ; +COMMIT; +SELECT a>>1, b>>1, c>>1 FROM t; +|"", "", "" +[2h30m0s 1m30s 1s] + +-- 440 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("5h"), + duration("3m"), + duration("2s"), + ), + ; +COMMIT; +SELECT a<<1, b<<1, c<<1 FROM t; +|"", "", "" +[10h0m0s 6m0s 4s] + +-- 441 +BEGIN TRANSACTION; + CREATE TABLE t (a duration); + INSERT INTO t VALUES ( + duration("257ns"), + ), + ; +COMMIT; +SELECT a & 255 FROM t; +|"" +[1ns] + +-- 442 +BEGIN TRANSACTION; + CREATE TABLE t (a duration); + INSERT INTO t VALUES ( + duration("1ns"), + ), + ; +COMMIT; +SELECT a ∨ 256 FROM t; +|"" +[257ns] + +-- 443 +BEGIN TRANSACTION; + CREATE TABLE t (a duration); + INSERT INTO t VALUES ( + duration(0x731), + ), + ; +COMMIT; +SELECT a &^ 0xd30 FROM t; +|"" +[513ns] + +-- 444 +BEGIN TRANSACTION; + CREATE TABLE t (a duration); + INSERT INTO t VALUES ( + duration("3h2m1s"), + ), + ; +COMMIT; +SELECT a % duration("2h"), a % duration("1m") FROM t; +|"", "" +[1h2m1s 1s] + +-- 445 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("5h"), + duration("3m"), + duration("2s"), + ), + ; +COMMIT; +SELECT a/2, b/2, c/2 FROM t; +|"", "", "" +[2h30m0s 1m30s 1s] + +-- 446 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("5h"), + duration("3m"), + duration("2s"), + ), + ; +COMMIT; +SELECT a*2, 2*b, c*2 FROM t; +|"", "", "" +[10h0m0s 6m0s 4s] + +-- 447 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("1ns"), + duration("3ns"), + duration("5ns"), + ), + ; +COMMIT; +SELECT ^a, ^b, ^c FROM t; +|"", "", "" +[-2ns -4ns -6ns] + +-- 448 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("1ns"), + duration("3ns"), + duration("5ns"), + ), + ; +COMMIT; +SELECT +a, +b, +c FROM t; +|"", "", "" +[1ns 3ns 5ns] + +-- 449 +BEGIN TRANSACTION; + CREATE TABLE t (a duration, b duration, c duration); + INSERT INTO t VALUES ( + duration("1ns"), + duration("3ns"), + duration("5ns"), + ), + ; +COMMIT; +SELECT -a, -b, -c FROM t; +|"", "", "" +[-1ns -3ns -5ns] + +-- 450 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b time, c time); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999994, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999995, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999996, "CET"), + ), + ; +COMMIT; +SELECT a > a, a > b, a > c, b > a, b > b, b > c, c > a, c > b, c > c FROM t; +|"", "", "", "", "", "", "", "", "" +[false false false true false false true true false] + +-- 451 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b time, c time); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999994, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999995, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999996, "CET"), + ), + ; +COMMIT; +SELECT a < a, a < b, a < c, b < a, b < b, b < c, c < a, c < b, c < c FROM t; +|"", "", "", "", "", "", "", "", "" +[false true true false false true false false false] + +-- 452 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b time, c time); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999994, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999995, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999996, "CET"), + ), + ; +COMMIT; +SELECT a <= a, a <= b, a <= c, b <= a, b <= b, b <= c, c <= a, c <= b, c <= c FROM t; +|"", "", "", "", "", "", "", "", "" +[true true true false true true false false true] + +-- 453 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b time, c time); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999994, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999995, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999996, "CET"), + ), + ; +COMMIT; +SELECT a >= a, a >= b, a >= c, b >= a, b >= b, b >= c, c >= a, c >= b, c >= c FROM t; +|"", "", "", "", "", "", "", "", "" +[true false false true true false true true true] + +-- 454 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b time, c time); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999994, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999995, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999996, "CET"), + ), + ; +COMMIT; +SELECT a != a, a != b, a != c, b != a, b != b, b != c, c != a, c != b, c != c FROM t; +|"", "", "", "", "", "", "", "", "" +[false true true true false true true true false] + +-- 455 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b time, c time); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999994, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999995, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999996, "CET"), + ), + ; +COMMIT; +SELECT a == a, a == b, a == c, b == a, b == b, b == c, c == a, c == b, c == c FROM t; +|"", "", "", "", "", "", "", "", "" +[true false false false true false false false true] + +-- 456 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b duration); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999999, "CET"), + duration("3h2m1s"), + ), + ; +COMMIT; +SELECT formatTime(timeIn(a+b, "UTC"), "2006-01-02 15:04:05.999999999 -0700") FROM t; +|"" +[2013-11-27 14:03:03.999999999 +0000] + +-- 457 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b duration); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999999, "CET"), + duration("3h2m1s"), + ), + ; +COMMIT; +SELECT formatTime(timeIn(b+a, "UTC"), "2006-01-02 15:04:05.999999999 -0700") FROM t; +|"" +[2013-11-27 14:03:03.999999999 +0000] + +-- 458 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b duration); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999999, "CET"), + duration("3h2m1s"), + ), + ; +COMMIT; +SELECT a+a FROM t; +||invalid operation + +-- 459 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b time); + INSERT INTO t VALUES ( + date(2013, 11, 27, 13, 2, 3, 999999999, "CET"), + date(2013, 11, 27, 12, 1, 2, 999999999, "CET"), + ), + ; +COMMIT; +SELECT a-b FROM t; +|"" +[1h1m1s] + +-- 460 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b duration); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999999, "CET"), + duration("3h2m1s"), + ), + ; +COMMIT; +SELECT formatTime(timeIn(a-b, "UTC"), "2006-01-02 15:04:05.999999999 -0700") FROM t; +|"" +[2013-11-27 07:59:01.999999999 +0000] + +-- 461 +BEGIN TRANSACTION; + CREATE TABLE t (a time, b duration); + INSERT INTO t VALUES ( + date(2013, 11, 27, 12, 1, 2, 999999999, "CET"), + duration("3h2m1s"), + ), + ; +COMMIT; +SELECT b-a FROM t; +||invalid operation + +-- 462 +BEGIN TRANSACTION; + CREATE TABLE t (a duration); + INSERT INTO t VALUES ( + duration("3h2m1.5s"), + ), + ; +COMMIT; +SELECT hours(a), minutes(a), seconds(a), nanoseconds(a) FROM t; +|"", "", "", "" +[3.03375 182.025 10921.5 10921500000000] + +-- 463 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES (now()-duration("1s")); +COMMIT; +SELECT a < now(), now() > a, a >= now(), now() <= a FROM t; +|"", "", "", "" +[true true false false] + +-- 464 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04pm (MST)", "Nov 27, 2013 at 2:07pm (CET)")), + (parseTime("2006-Jan-02", "2013-Nov-27")), + ; +COMMIT; +SELECT formatTime(timeIn(a, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as a FROM t ORDER BY a; +|"a" +[2013-11-27 00:00:00 +0000] +[2013-11-27 13:07:00 +0000] + +-- 465 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04pm (MST)", "Nov 27, 2013 at 2:07pm (CET)")), + (parseTime("2006-Jan-02", "2013-Nov-27")), + ; +COMMIT; +SELECT hour(timeIn(a, "UTC")) AS y FROM t ORDER BY y; +|"y" +[0] +[13] + +-- 466 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04pm (MST)", "Nov 27, 2013 at 2:07pm (CET)")), + (parseTime("2006-Jan-02", "2013-Nov-27")), + ; +COMMIT; +SELECT minute(a) AS y FROM t ORDER BY y; +|"y" +[0] +[7] + +-- 467 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04:05pm (MST)", "Nov 27, 2013 at 2:07:31pm (CET)")), + (parseTime("2006-Jan-02", "2013-Nov-27")), + ; +COMMIT; +SELECT second(a) AS y FROM t ORDER BY y; +|"y" +[0] +[31] + +-- 468 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04:05pm (MST)", "Nov 27, 2013 at 2:07:31.123456789pm (CET)")), + (parseTime("2006-Jan-02", "2013-Nov-27")), + ; +COMMIT; +SELECT nanosecond(a) AS y FROM t ORDER BY y; +|"y" +[0] +[123456789] + +-- 469 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04:05pm (MST)", "Nov 27, 2013 at 2:07:31.123456789pm (CET)")), + (parseTime("2006-Jan-02", "2014-Nov-28")), + ; +COMMIT; +SELECT year(a) AS y FROM t ORDER BY y; +|"y" +[2013] +[2014] + +-- 470 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04:05pm (MST)", "Nov 27, 2013 at 2:07:31.123456789pm (CET)")), + (parseTime("2006-Jan-02", "2014-Nov-28")), + ; +COMMIT; +SELECT day(a) AS y FROM t ORDER BY y; +|"y" +[27] +[28] + +-- 471 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04:05pm (MST)", "Nov 27, 2013 at 2:07:31.123456789pm (CET)")), + (parseTime("2006-Jan-02", "2014-Dec-28")), + ; +COMMIT; +SELECT month(a) AS y FROM t ORDER BY y; +|"y" +[11] +[12] + +-- 472 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04:05pm (MST)", "Nov 27, 2013 at 2:07:31.123456789pm (CET)")), + (parseTime("2006-Jan-02", "2013-Sep-08")), + ; +COMMIT; +SELECT weekday(a) AS y FROM t ORDER BY y; +|"y" +[0] +[3] + +-- 473 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04:05pm (MST)", "Feb 1, 2013 at 2:07:31.123456789pm (CET)")), + (parseTime("2006-Jan-02", "2014-Feb-02")), + ; +COMMIT; +SELECT yearDay(a) AS y FROM t ORDER BY y; +|"y" +[32] +[33] + +-- 474 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04pm (MST)", "Feb 1, 2013 at 2:07pm (CET)")), + (parseTime("2006-Jan-02", "2014-Feb-02")), + ; +COMMIT; +SELECT timeIn(a, ""), timeIn(a, "UTC") AS y FROM t ORDER BY y; +|"", "y" +[2013-02-01 13:07:00 +0000 UTC 2013-02-01 13:07:00 +0000 UTC] +[2014-02-02 00:00:00 +0000 UTC 2014-02-02 00:00:00 +0000 UTC] + +-- 475 +BEGIN TRANSACTION; + CREATE TABLE t (a time); + INSERT INTO t VALUES + (parseTime("Jan 2, 2006 at 3:04pm (MST)", "Feb 1, 2013 at 2:07pm (CET)")), + (parseTime("2006-Jan-02", "2014-Feb-02")), + ; +COMMIT; +SELECT formatTime(timeIn(a, "UTC"), "Jan 2, 2006 at 3:04pm (UTC)") AS y FROM t ORDER BY y; +|"y" +[Feb 1, 2013 at 1:07pm (UTC)] +[Feb 2, 2014 at 12:00am (UTC)] + +-- 476 +BEGIN TRANSACTION; + BEGIN TRANSACTION; + COMMIT; +COMMIT; +SELECT * FROM t; +||does not exist + +-- 477 +BEGIN TRANSACTION; + BEGIN TRANSACTION; + ROLLBACK; +COMMIT; +SELECT * FROM t; +||does not exist + +-- 478 // https://github.com/cznic/ql/issues/23 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b string, t time); + INSERT INTO t VALUES (1, "a", parseTime("Jan 2, 2006 at 3:04pm (MST)", "Jan 12, 2014 at 6:26pm (CET)")); + INSERT INTO t VALUES (2, "b", parseTime("Jan 2, 2006 at 3:04pm (MST)", "Jan 12, 2014 at 6:27pm (CET)")); + UPDATE t b = "hello" WHERE a == 1; +COMMIT; +SELECT a, b, formatTime(timeIn(t, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as t FROM t; +|"a", "b", "t" +[2 b 2014-01-12 17:27:00 +0000] +[1 hello 2014-01-12 17:26:00 +0000] + +-- 479 // https://github.com/cznic/ql/issues/23 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b string, t time); + INSERT INTO t VALUES (1, "a", parseTime("Jan 2, 2006 at 3:04pm (MST)", "Jan 12, 2014 at 6:26pm (CET)")); + INSERT INTO t VALUES (2, "b", parseTime("Jan 2, 2006 at 3:04pm (MST)", "Jan 12, 2014 at 6:27pm (CET)")); + UPDATE t + b = "hello", + t = parseTime("Jan 2, 2006 at 3:04pm (MST)", "Jan 12, 2014 at 6:28pm (CET)"), + WHERE a == 1; +COMMIT; +SELECT a, b, formatTime(timeIn(t, "UTC"), "2006-01-02 15:04:05.999999999 -0700") as t FROM t; +|"a", "b", "t" +[2 b 2014-01-12 17:27:00 +0000] +[1 hello 2014-01-12 17:28:00 +0000] + +-- 480 // https://github.com/cznic/ql/issues/23 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b string, d duration); + INSERT INTO t VALUES (1, "a", duration("1m")); + INSERT INTO t VALUES (2, "b", duration("2m")); + UPDATE t b = "hello" WHERE a == 1; +COMMIT; +SELECT * FROM t; +|"a", "b", "d" +[2 b 2m0s] +[1 hello 1m0s] + +-- 481 // https://github.com/cznic/ql/issues/23 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b string, d duration); + INSERT INTO t VALUES (1, "a", duration("1m")); + INSERT INTO t VALUES (2, "b", duration("2m")); + UPDATE t + b = "hello", + d = duration("3m"), + WHERE a == 1; +COMMIT; +SELECT * FROM t; +|"a", "b", "d" +[2 b 2m0s] +[1 hello 3m0s] + +-- 482 // https://github.com/cznic/ql/issues/24 +BEGIN TRANSACTION; + CREATE TABLE t (c complex128); + INSERT INTO t VALUES + (2+complex128(1)), + (22+complex(0, 1)), + ; +COMMIT; +SELECT * FROM t ORDER BY real(c); +|"c" +[(3+0i)] +[(22+1i)] + +-- 483 +BEGIN TRANSACTION; + CREATE TABLE t (s string, substr string); + INSERT INTO t VALUES + ("seafood", "foo"), + ("seafood", "bar"), + ("seafood", ""), + ("", ""), + ; +COMMIT; +SELECT id() as i, contains(42, substr) FROM t ORDER BY i; +||invalid .* 42 + +-- 484 +BEGIN TRANSACTION; + CREATE TABLE t (s string, substr string); + INSERT INTO t VALUES + ("seafood", "foo"), + ("seafood", "bar"), + ("seafood", ""), + ("", ""), + ; +COMMIT; +SELECT id() as i, contains(s, true) FROM t ORDER BY i; +||invalid .* true + +-- 485 +BEGIN TRANSACTION; + CREATE TABLE t (s string, substr string); + INSERT INTO t VALUES + ("seafood", "foo"), + ("seafood", "bar"), + ("seafood", ""), + ("", ""), + ("", NULL), + ("foo", NULL), + (NULL, ""), + (NULL, "foo"), + (NULL, NULL), + ; +COMMIT; +SELECT id() as i, contains(s, substr) FROM t ORDER BY i; +|"i", "" +[1 true] +[2 false] +[3 true] +[4 true] +[5 ] +[6 ] +[7 ] +[8 ] +[9 ] + +-- 486 +BEGIN TRANSACTION; + CREATE TABLE t (s string, prefix string); + INSERT INTO t VALUES + ("", ""), + ("f", ""), + ("", "foo"), + ("f", "foo"), + ("fo", "foo"), + ("foo", "foo"), + ("fooo", "foo"), + ; +COMMIT; +SELECT id() as i, hasPrefix(42, prefix) FROM t ORDER BY i; +||invalid .* 42 + +-- 487 +BEGIN TRANSACTION; + CREATE TABLE t (s string, prefix string); + INSERT INTO t VALUES + ("", ""), + ("f", ""), + ("", "foo"), + ("f", "foo"), + ("fo", "foo"), + ("foo", "foo"), + ("fooo", "foo"), + ; +COMMIT; +SELECT id() as i, hasPrefix(s, false) FROM t ORDER BY i; +||invalid .* false + +-- 488 +BEGIN TRANSACTION; + CREATE TABLE t (s string, prefix string); + INSERT INTO t VALUES + ("", ""), + ("f", ""), + ("", "foo"), + ("f", "foo"), + ("fo", "foo"), + ("foo", "foo"), + ("fooo", "foo"), + ("", NULL), + ("foo", NULL), + (NULL, ""), + (NULL, "foo"), + (NULL, NULL), + ; +COMMIT; +SELECT id() as i, hasPrefix(s, prefix) FROM t ORDER BY i; +|"i", "" +[1 true] +[2 true] +[3 false] +[4 false] +[5 false] +[6 true] +[7 true] +[8 ] +[9 ] +[10 ] +[11 ] +[12 ] + +-- 489 +BEGIN TRANSACTION; + CREATE TABLE t (s string, suffix string); + INSERT INTO t VALUES + ("", ""), + ("f", ""), + ("x", "foo"), + ("xf", "foo"), + ("xfo", "foo"), + ("xfoo", "foo"), + ("xfooo", "foo"), + ; +COMMIT; +SELECT id() as i, hasSuffix(42, suffix) FROM t ORDER BY i; +||invalid .* 42 + +-- 490 +BEGIN TRANSACTION; + CREATE TABLE t (s string, suffix string); + INSERT INTO t VALUES + ("", ""), + ("f", ""), + ("x", "foo"), + ("xf", "foo"), + ("xfo", "foo"), + ("xfoo", "foo"), + ("xfooo", "foo"), + ; +COMMIT; +SELECT id() as i, hasSuffix(s, true) FROM t ORDER BY i; +||invalid .* true + +-- 491 +BEGIN TRANSACTION; + CREATE TABLE t (s string, suffix string); + INSERT INTO t VALUES + ("", ""), + ("f", ""), + ("x", "foo"), + ("xf", "foo"), + ("xfo", "foo"), + ("xfoo", "foo"), + ("xfooo", "foo"), + ("", NULL), + ("foo", NULL), + (NULL, ""), + (NULL, "foo"), + (NULL, NULL), + ; +COMMIT; +SELECT id() as i, hasSuffix(s, suffix) FROM t ORDER BY i; +|"i", "" +[1 true] +[2 true] +[3 false] +[4 false] +[5 false] +[6 true] +[7 false] +[8 ] +[9 ] +[10 ] +[11 ] +[12 ] + +-- 492 // issue #27 +BEGIN TRANSACTION; + DROP TABLE nonexistent; +COMMIT; +||does not exist + +-- 493 // issue #27 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + DROP TABLE IF EXISTS nonexistent; +COMMIT; +SELECT * FROM t; +|"i" + +-- 494 // issue #27 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE t (i int); +COMMIT; +||exist + +-- 495 // issue #27 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE IF NOT EXISTS t (s string); +COMMIT; +SELECT * FROM t; +|"i" + +-- 496 // issue #28 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); + ALTER TABLE t ADD s string; +COMMIT; +SELECT * FROM t; +|"i", "s" +[42 ] + +-- 497 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +BEGIN TRANSACTION; + INSERT INTO t VALUES(1000); + BEGIN TRANSACTION; + INSERT INTO t VALUES(2000); + COMMIT; + INSERT INTO t VALUES(3000); +COMMIT; +SELECT * FROM t; +|"i" +[3000] +[2000] +[1000] + +-- 498 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +BEGIN TRANSACTION; + INSERT INTO t VALUES(1000); + BEGIN TRANSACTION; + INSERT INTO t VALUES(2000); + ROLLBACK; + INSERT INTO t VALUES(3000); +COMMIT; +SELECT * FROM t; +|"i" +[3000] +[1000] + +-- 499 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + INSERT INTO t VALUES(42, "foo"); + ALTER TABLE t DROP COLUMN i; +COMMIT; +SELECT * FROM t; +|"s" +[foo] + +-- 500 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + INSERT INTO t VALUES(42, "foo"); + ALTER TABLE t DROP COLUMN s; +COMMIT; +SELECT * FROM t; +|"i" +[42] + +-- 501 // new spec rule: table must have at least 1 column +BEGIN TRANSACTION; + CREATE TABLE t (c int); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t DROP COLUMN c; +COMMIT; +SELECT * FROM t; +||cannot drop.*column + +-- 502 // fixed bug +BEGIN TRANSACTION; + CREATE TABLE t (c int, s string); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t DROP COLUMN s; +ROLLBACK; +SELECT * FROM t; +|"c", "s" + +-- 503 // fixed bug +BEGIN TRANSACTION; + CREATE TABLE t (c int, s string); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t ADD b bool; +ROLLBACK; +SELECT * FROM t; +|"c", "s" + +-- 504 // fixed bug +BEGIN TRANSACTION; + CREATE TABLE t (c int, s string); +COMMIT; +BEGIN TRANSACTION; + DROP TABLE t; +ROLLBACK; +SELECT * FROM t; +|"c", "s" + +-- 505 // fixed bug +BEGIN TRANSACTION; + CREATE INDEX x ON t (qty()); +COMMIT; +||undefined.* qty + +-- 506 +BEGIN TRANSACTION; + CREATE INDEX x ON t (qty); +COMMIT; +||table.*not exist + +-- 507 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (qty); +COMMIT; +||column.*not exist + +-- 508 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (id()); +COMMIT; +SELECT * FROM t; +|"c" + +-- 509 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX y ON t (c); +COMMIT; +SELECT * FROM t; +|"c" + +-- 510 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (id()); + CREATE INDEX y ON t (id()); +COMMIT; +SELECT * FROM t; +||already + +-- 511 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (id()); + CREATE INDEX x ON t (c); +COMMIT; +SELECT * FROM t; +||already + +-- 512 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (id()); + CREATE INDEX y ON t (c); +COMMIT; +SELECT * FROM t; +|"c" + +-- 513 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX y ON t (c); + CREATE INDEX x ON t (id()); +COMMIT; +SELECT * FROM t; +|"c" + +-- 514 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (id()); + INSERT INTO t VALUES(42); +COMMIT; +SELECT * FROM t; +|"c" +[42] + +-- 515 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + INSERT INTO t VALUES(42); + CREATE INDEX x ON t (id()); +COMMIT; +SELECT * FROM t; +|"c" +[42] + +-- 516 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + INSERT INTO t VALUES(42); + INSERT INTO t VALUES(24); + CREATE INDEX x ON t (id()); + INSERT INTO t VALUES(1); + CREATE INDEX i ON t (c); + INSERT INTO t VALUES(999); +COMMIT; +SELECT * FROM t ORDER BY id(); +|"c" +[42] +[24] +[1] +[999] + +-- 517 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + INSERT INTO t VALUES(42); + INSERT INTO t VALUES(24); + CREATE INDEX x ON t (id()); + INSERT INTO t VALUES(1); + CREATE INDEX i ON t (c); + INSERT INTO t VALUES(999); +COMMIT; +SELECT * FROM t ORDER BY c; +|"c" +[1] +[24] +[42] +[999] + +-- 518 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX xid ON t (id()); + INSERT INTO t VALUES(42); + INSERT INTO t VALUES(24); + CREATE INDEX ii ON t (i); + INSERT INTO t VALUES(1); + INSERT INTO t VALUES(999); + UPDATE t i = 240 WHERE i == 24; + DELETE FROM t WHERE i == 240; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[42] +[999] + +-- 519 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX i ON t (i); +COMMIT; +||collision: i + +-- 520 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX t ON t (i); +COMMIT; +||collision: t + +-- 521 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (s string); + CREATE INDEX u ON t (i); +COMMIT; +||collision.*: u + +-- 522 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (s string); + CREATE INDEX z ON t (i); + CREATE INDEX z ON u (s); +COMMIT; +||already + +-- 523 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX u ON u (s); +COMMIT; +||collision: u + +-- 524 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX v ON u (v); +COMMIT; +||collision: v + +-- 525 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX s ON t (i); +COMMIT; +||collision.*: s + +-- 526 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX id ON t (i); +COMMIT; +SELECT * FROM t; +|"i" + +-- 527 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + CREATE TABLE x (s string); +COMMIT; +||table t.*index x + +-- 528 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +BEGIN TRANSACTION; + INSERT INTO t VALUES(1000); + BEGIN TRANSACTION; + INSERT INTO t VALUES(2000); + ROLLBACK; + INSERT INTO t VALUES(3000); +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1000] +[3000] + +-- 529 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + TRUNCATE TABLE t; +COMMIT; +SELECT * FROM t; +|"i" + +-- 530 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + DELETE FROM t; +COMMIT; +SELECT * FROM t; +|"i" + +-- 531 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX xi ON t (i); + INSERT INTO t VALUES (42, "foo"); + ALTER TABLE t DROP COLUMN i; + INSERT INTO t VALUES ("bar"); +COMMIT; +SELECT * FROM t ORDER BY s; +|"s" +[bar] +[foo] + +-- 532 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX xs ON t (s); + INSERT INTO t VALUES (42, "foo"); + ALTER TABLE t DROP COLUMN i; + INSERT INTO t VALUES ("bar"); +COMMIT; +SELECT * FROM t ORDER BY s; +|"s" +[bar] +[foo] + +-- 533 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX xi ON t (i); + CREATE INDEX xs ON t (s); + INSERT INTO t VALUES (42, "foo"); + ALTER TABLE t DROP COLUMN i; + INSERT INTO t VALUES ("bar"); +COMMIT; +SELECT * FROM t ORDER BY s; +|"s" +[bar] +[foo] + +-- 534 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX xi ON t (i); + INSERT INTO t VALUES (42, "foo"); + ALTER TABLE t DROP COLUMN s; + INSERT INTO t VALUES (24); +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[24] +[42] + +-- 535 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX xs ON t (s); + INSERT INTO t VALUES (42, "foo"); + ALTER TABLE t DROP COLUMN s; + INSERT INTO t VALUES (24); +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[24] +[42] + +-- 536 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX xi ON t (i); + CREATE INDEX xs ON t (s); + INSERT INTO t VALUES (42, "foo"); + ALTER TABLE t DROP COLUMN s; + INSERT INTO t VALUES (24); +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[24] +[42] + +-- 537 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + INSERT INTO t VALUES (42, "foo"); + ALTER TABLE t DROP COLUMN i; +COMMIT; +SELECT * FROM t; +|"s" +[foo] + +-- 538 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + INSERT INTO t VALUES (42, "foo"); + ALTER TABLE t DROP COLUMN s; +COMMIT; +SELECT * FROM t; +|"i" +[42] + +-- 539 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + INSERT INTO t VALUES (42, "foo"); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t DROP COLUMN i; +COMMIT; +SELECT * FROM t; +|"s" +[foo] + +-- 540 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + INSERT INTO t VALUES (42, "foo"); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t DROP COLUMN s; +COMMIT; +SELECT * FROM t; +|"i" +[42] + +-- 541 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42, "foo"); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t DROP COLUMN s; +COMMIT; +BEGIN TRANSACTION; + INSERT INTO t VALUES (24); +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[24] +[42] + +-- 542 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42, "foo"); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t DROP COLUMN i; +COMMIT; +BEGIN TRANSACTION; + INSERT INTO t VALUES ("bar"); +COMMIT; +SELECT * FROM t ORDER BY s; +|"s" +[bar] +[foo] + +-- 543 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + INSERT INTO t SELECT 10*i FROM t; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[42] +[420] + +-- 544 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + INSERT INTO t SELECT 10*i FROM t; + DROP INDEX none; +COMMIT; +||index none does not exist + +-- 545 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + INSERT INTO t SELECT 10*i FROM t; + DROP INDEX x; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[42] +[420] + +-- 546 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + INSERT INTO t SELECT 10*i FROM t; +COMMIT; +BEGIN TRANSACTION; + DROP INDEX x; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[42] +[420] + +-- 547 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + ALTER TABLE t ADD s string; +COMMIT; +SELECT * FROM t; +|"i", "s" + +-- 548 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + ALTER TABLE t ADD s string; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i", "s" + +-- 549 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t ADD s string; +COMMIT; +SELECT * FROM t ORDER BY s; +|"i", "s" + +-- 550 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); +COMMIT; +SELECT * FROM x; +|"x" +[42] + +-- 551 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (42); + INSERT INTO t VALUES (420); +COMMIT; +SELECT * FROM x; +|"x" +[42] +[420] + +-- 552 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (420); + INSERT INTO t VALUES (42); +COMMIT; +SELECT * FROM x; +|"x" +[42] +[420] + +-- 553 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (420); + INSERT INTO t VALUES (42); + INSERT INTO t VALUES (100); +COMMIT; +SELECT * FROM x; +|"x" +[42] +[100] +[420] + +-- 554 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (420); + INSERT INTO t VALUES (42); + INSERT INTO t VALUES (100); + DELETE FROM t WHERE i == 100; +COMMIT; +SELECT * FROM x; +|"x" +[42] +[420] + +-- 555 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (420); + INSERT INTO t VALUES (42); + INSERT INTO t VALUES (100); +COMMIT; +BEGIN TRANSACTION; + DELETE FROM t WHERE i == 100; +COMMIT; +SELECT * FROM x; +|"x" +[42] +[420] + +-- 556 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); + INSERT INTO t VALUES (3); +COMMIT; +SELECT * FROM x; +|"x" +[1] +[2] +[3] + +-- 557 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (3); + INSERT INTO t VALUES (2); +COMMIT; +SELECT * FROM x; +|"x" +[1] +[2] +[3] + +-- 558 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (2); + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (3); +COMMIT; +SELECT * FROM x; +|"x" +[1] +[2] +[3] + +-- 559 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (2); + INSERT INTO t VALUES (3); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * FROM x; +|"x" +[1] +[2] +[3] + +-- 560 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (3); + INSERT INTO t VALUES (1); + INSERT INTO t VALUES (2); +COMMIT; +SELECT * FROM x; +|"x" +[1] +[2] +[3] + +-- 561 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (3); + INSERT INTO t VALUES (2); + INSERT INTO t VALUES (1); +COMMIT; +SELECT * FROM x; +|"x" +[1] +[2] +[3] + +-- 562 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (bigint(1) << 100); + INSERT INTO t VALUES (bigint(1) << 100 - 1); + INSERT INTO t VALUES (bigint(1) << (256*8)); + INSERT INTO t VALUES (bigint(1) << 100 + 1); + INSERT INTO t VALUES (bigint(1) << 10); +COMMIT; +SELECT * FROM x; +|"x" +[1024] +[1267650600228229401496703205375] +[1267650600228229401496703205376] +[1267650600228229401496703205377] +[32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656] + +-- 563 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (bigint(1) << 100); + INSERT INTO t VALUES (bigint(1) << 100 - 1); + INSERT INTO t VALUES (bigint(1) << (256*8)); + INSERT INTO t VALUES (bigint(1) << 100 + 1); + INSERT INTO t VALUES (bigint(1) << 10); +COMMIT; +BEGIN TRANSACTION; + DELETE FROM t WHERE i == (bigint(1) << (256*8)); +COMMIT; +SELECT * FROM x; +|"x" +[1024] +[1267650600228229401496703205375] +[1267650600228229401496703205376] +[1267650600228229401496703205377] + +-- 564 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (bigint(1) << 100); + INSERT INTO t VALUES (bigint(1) << 100 - 1); + INSERT INTO t VALUES (bigint(1) << 100 + 1); + INSERT INTO t VALUES (bigint(1) << 10); +COMMIT; +BEGIN TRANSACTION; + UPDATE t + i = i+10, + WHERE i == bigint(1) << 100; +COMMIT; +SELECT * FROM x; +|"x" +[1024] +[1267650600228229401496703205375] +[1267650600228229401496703205377] +[1267650600228229401496703205386] + +-- 565 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (bigint(1) << 100); + INSERT INTO t VALUES (bigint(1) << 100 - 1); + INSERT INTO t VALUES (bigint(1) << (256*8)); + INSERT INTO t VALUES (bigint(1) << 100 + 1); + INSERT INTO t VALUES (bigint(1) << 10); +COMMIT; +BEGIN TRANSACTION; + UPDATE t + i = 42, + WHERE i == bigint(1) << (256*8); +COMMIT; +SELECT * FROM x; +|"x" +[42] +[1024] +[1267650600228229401496703205375] +[1267650600228229401496703205376] +[1267650600228229401496703205377] + +-- 566 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (bigint(1) << 100); + INSERT INTO t VALUES (bigint(1) << 100 - 1); + INSERT INTO t VALUES (bigint(42)); + INSERT INTO t VALUES (bigint(1) << 100 + 1); + INSERT INTO t VALUES (bigint(1) << 10); +COMMIT; +BEGIN TRANSACTION; + UPDATE t + i = bigint(1) << (256*8), + WHERE i == 42; +COMMIT; +SELECT * FROM x; +|"x" +[1024] +[1267650600228229401496703205375] +[1267650600228229401496703205376] +[1267650600228229401496703205377] +[32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656] + +-- 567 +BEGIN TRANSACTION; + CREATE TABLE t (b blob); + CREATE INDEX x ON t (b); + INSERT INTO t VALUES (blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" // > shortBlob + )); + DROP TABLE t; +COMMIT; +SELECT * FROM t; +||does not exist + +-- 568 +BEGIN TRANSACTION; + CREATE TABLE t (b blob); + CREATE INDEX x ON t (b); + INSERT INTO t VALUES (blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" // > shortBlob + )); + DROP TABLE t; +COMMIT; +BEGIN TRANSACTION; + DROP TABLE t; +COMMIT; +SELECT * FROM t; +||does not exist + +-- 569 +BEGIN TRANSACTION; + CREATE TABLE t (b blob); + CREATE INDEX x ON t (b); + INSERT INTO t VALUES (blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" // > shortBlob + )); + DROP INDEX x; +COMMIT; +SELECT len(string(b)) AS n FROM t; +|"n" +[320] + +-- 570 +BEGIN TRANSACTION; + CREATE TABLE t (b blob); + CREATE INDEX x ON t (b); + INSERT INTO t VALUES (blob( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"+ + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" // > shortBlob + )); +COMMIT; +BEGIN TRANSACTION; + DROP INDEX x; +COMMIT; +SELECT len(string(b)) AS n FROM t; +|"n" +[320] + +-- 571 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); + ALTER TABLE t ADD s string; +COMMIT; +SELECT * FROM t; +|"i", "s" +[42 ] + +-- 572 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t ADD s string; +COMMIT; +SELECT * FROM t; +|"i", "s" +[42 ] + +-- 573 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM q.t; +||expected .*where + +-- 574 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); +COMMIT; +SELECT * FROM t AS u; +|"i" +[42] + +-- 575 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); +COMMIT; +SELECT u.x FROM t AS u; +||unknown field u.x + +-- 576 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); +COMMIT; +SELECT u.i FROM t AS u; +||unknown field u.i + +-- 577 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); +COMMIT; +SELECT i FROM t AS u; +|"i" +[42] + +-- 578 +BEGIN TRANSACTION; + CREATE TABLE t (i int, b bool); + CREATE INDEX x ON t (b); + INSERT INTO t VALUES(24, false); + INSERT INTO t VALUES(333, NULL); + INSERT INTO t VALUES(42, true); + INSERT INTO t VALUES(240, false); + INSERT INTO t VALUES(420, true); +COMMIT; +SELECT i FROM t WHERE b ORDER BY i; +|"i" +[42] +[420] + +-- 579 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(10, "foo"); +COMMIT; +SELECT * FROM t WHERE i < "30"; +||type string.*type int64 + +-- 580 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM x; +|"x" +[] +[] +[10] +[10] +[20] +[20] +[30] +[30] +[40] +[40] +[50] +[50] + +-- 581 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE i < 30; +|"i" +[10] +[20] +[20] +[10] + +-- 582 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE i < 30; +|"i" +[10] +[10] +[20] +[20] + +-- 583 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i <= 30; +|"i" +[10] +[20] +[30] +[30] +[20] +[10] + +-- 584 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i <= 30; +|"i" +[10] +[10] +[20] +[20] +[30] +[30] + +-- 585 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE i == 30; +|"i" +[30] +[30] + +-- 586 // index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE i == 30; +|"i" +[30] +[30] + +-- 587 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i >= 30; +|"i" +[50] +[40] +[30] +[30] +[40] +[50] + +-- 588 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i >= 30; +|"i" +[30] +[30] +[40] +[40] +[50] +[50] + +-- 589 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i > 30; +|"i" +[50] +[40] +[40] +[50] + +-- 590 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i > 30; +|"i" +[40] +[40] +[50] +[50] + +-- 591 +BEGIN TRANSACTION; + CREATE TABLE t (i int, b bool); + INSERT INTO t VALUES(24, false); + INSERT INTO t VALUES(333, NULL); + INSERT INTO t VALUES(42, true); + INSERT INTO t VALUES(240, false); + INSERT INTO t VALUES(420, true); +COMMIT; +SELECT i FROM t WHERE !b ORDER BY i; +|"i" +[24] +[240] + +-- 592 +BEGIN TRANSACTION; + CREATE TABLE t (i int, b bool); + CREATE INDEX x ON t (b); + INSERT INTO t VALUES(24, false); + INSERT INTO t VALUES(333, NULL); + INSERT INTO t VALUES(42, true); + INSERT INTO t VALUES(240, false); + INSERT INTO t VALUES(420, true); +COMMIT; +SELECT i FROM t WHERE !b ORDER BY i; +|"i" +[24] +[240] + +-- 593 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE i < $1; // 30 +|"i" +[10] +[10] +[20] +[20] + +-- 594 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i <= $1; // 30 +|"i" +[10] +[10] +[20] +[20] +[30] +[30] + +-- 595 // index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE i == $1; // 30 +|"i" +[30] +[30] + +-- 596 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i >= $1; // 30 +|"i" +[30] +[30] +[40] +[40] +[50] +[50] + +-- 597 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i > $1; // 30 +|"i" +[40] +[40] +[50] +[50] + +-- 598 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE $1 > i; // 30 +|"i" +[10] +[10] +[20] +[20] + +-- 599 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE $1 >= i; // 30 +|"i" +[10] +[10] +[20] +[20] +[30] +[30] + +-- 600 // index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE $1 == i; // 30 +|"i" +[30] +[30] + +-- 601 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE $1 <= i; // 30 +|"i" +[30] +[30] +[40] +[40] +[50] +[50] + +-- 602 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE $1 < i; // 30 +|"i" +[40] +[40] +[50] +[50] + +-- 603 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE 30 > i; +|"i" +[10] +[10] +[20] +[20] + +-- 604 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE 30 >= i; +|"i" +[10] +[10] +[20] +[20] +[30] +[30] + +-- 605 // index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT i FROM t WHERE 30 == i; +|"i" +[30] +[30] + +-- 606 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE 30 <= i; +|"i" +[30] +[30] +[40] +[40] +[50] +[50] + +-- 607 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE 30 < i; +|"i" +[40] +[40] +[50] +[50] + +-- 608 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i < 30; +|"i" +[20] +[10] + +-- 609 // ordered -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE UNIQUE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i < 30; +|"i" +[10] +[20] + +-- 610 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE UNIQUE INDEX x ON t (i); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM t WHERE i < 30; +||duplicate + +-- 611 // Issue #34 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); +COMMIT; +SELECT * FROM t WHERE i == $0; +||parameter.*non zero + +-- 612 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE __Table (i int); +COMMIT; +SELECT * FROM t; +||system table + +-- 613 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE __Column (i int); +COMMIT; +SELECT * FROM t; +||system table + +-- 614 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE __Index (i int); +COMMIT; +SELECT * FROM t; +||system table + +-- 615 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + DROP TABLE __Table; +COMMIT; +SELECT * FROM t; +||system table + +-- 616 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + DROP TABLE __Column; +COMMIT; +SELECT * FROM t; +||system table + +-- 617 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + DROP TABLE __Index; +COMMIT; +SELECT * FROM t; +||system table + +-- 618 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX __Table ON t (i); +COMMIT; +SELECT * FROM t; +||system table + +-- 619 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX __Column ON t (i); +COMMIT; +SELECT * FROM t; +||system table + +-- 620 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX __Index ON t (i); +COMMIT; +SELECT * FROM t; +||system table + +-- 621 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON __Table (Name); +COMMIT; +SELECT * FROM t; +||system table + +-- 622 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON __Column (Name); +COMMIT; +SELECT * FROM t; +||system table + +-- 623 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON __Index (Name); +COMMIT; +SELECT * FROM t; +||system table + +-- 624 +SELECT * FROM __Table; +|"Name", "Schema" + +-- 625 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); +COMMIT; +SELECT * FROM __Table ORDER BY Name; +|"Name", "Schema" +[t CREATE TABLE t (i int64, s string);] + +-- 626 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE TABLE u (b bool, i bigint, t time, d duration); +COMMIT; +SELECT * FROM __Table ORDER BY Name; +|"Name", "Schema" +[t CREATE TABLE t (i int64, s string);] +[u CREATE TABLE u (b bool, i bigint, t time, d duration);] + +-- 627 +SELECT * FROM __Column; +|"TableName", "Ordinal", "Name", "Type" + +-- 628 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); +COMMIT; +SELECT * FROM __Column ORDER BY TableName, Name; +|"TableName", "Ordinal", "Name", "Type" +[t 1 i int64] +[t 2 s string] + +-- 629 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE TABLE u (b bool, i bigint, t time, d duration); +COMMIT; +SELECT * FROM __Column ORDER BY TableName, Ordinal; +|"TableName", "Ordinal", "Name", "Type" +[t 1 i int64] +[t 2 s string] +[u 1 b bool] +[u 2 i bigint] +[u 3 t time] +[u 4 d duration] + +-- 630 +SELECT * FROM __Index; +|"TableName", "ColumnName", "Name", "IsUnique" + +-- 631 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); +COMMIT; +SELECT * FROM __Index ORDER BY TableName, Name; +|"TableName", "ColumnName", "Name", "IsUnique" + +-- 632 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (i); +COMMIT; +SELECT * FROM __Index WHERE !hasPrefix(TableName, "__") ORDER BY TableName, ColumnName, Name; +|"TableName", "ColumnName", "Name", "IsUnique" +[t i x false] + +-- 633 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (i); + CREATE INDEX id ON t (id()); + CREATE TABLE u (b bool, i bigint, t time, d duration); +COMMIT; +SELECT * FROM __Index WHERE !hasPrefix(TableName, "__") ORDER BY TableName, ColumnName, Name; +|"TableName", "ColumnName", "Name", "IsUnique" +[t i x false] +[t id() id false] + +-- 634 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (i); + CREATE INDEX id ON t (id()); + CREATE TABLE u (b bool, i bigint, t time, d duration); + CREATE INDEX z ON u (t); + CREATE UNIQUE INDEX y ON u (i); +COMMIT; +SELECT * FROM __Index WHERE !hasPrefix(TableName, "__") ORDER BY TableName, ColumnName, Name; +|"TableName", "ColumnName", "Name", "IsUnique" +[t i x false] +[t id() id false] +[u i y true] +[u t z false] + +-- 635 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (i); + CREATE INDEX id ON t (id()); + CREATE TABLE u (b bool, i bigint, t time, d duration); + CREATE INDEX z ON u (t); + CREATE UNIQUE INDEX y ON u (i); +COMMIT; +SELECT c.TableName, c.Ordinal, c.Name +FROM __Table AS t, __Column AS c +WHERE t.Name == "u" && t.Name == c.TableName +ORDER BY c.Ordinal; +|"c.TableName", "c.Ordinal", "c.Name" +[u 1 b] +[u 2 i] +[u 3 t] +[u 4 d] + +-- 636 // https://github.com/cznic/ql/issues/36 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + INSERT INTO t VALUES (1, "test"); +COMMIT; +SELECT * FROM t WHERE s == "test"; +|"i", "s" +[1 test] + +-- 637 // https://github.com/cznic/ql/issues/36 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + INSERT INTO t VALUES (1, "test"); + CREATE INDEX idx_s ON t (s); +COMMIT; +SELECT * FROM t WHERE s == "test"; +|"i", "s" +[1 test] + +-- 638 // https://github.com/cznic/ql/issues/37 +BEGIN TRANSACTION; + CREATE TABLE artist (id int64, name string); + CREATE TABLE data_types (id int64, _uint int64, _uint8 int64, _uint16 + int64, _uint32 int64, _uint64 int64, _int int64, _int8 int64, + _int16 int64, _int32 int64, _int64 int64, _float32 float32, + _float64 float64, _bool bool, _string string, _date time, _time + time); +COMMIT; +SELECT * FROM __Table ORDER BY Name; // Must sort, map range is not deterministic. +|"Name", "Schema" +[artist CREATE TABLE artist (id int64, name string);] +[data_types CREATE TABLE data_types (id int64, _uint int64, _uint8 int64, _uint16 int64, _uint32 int64, _uint64 int64, _int int64, _int8 int64, _int16 int64, _int32 int64, _int64 int64, _float32 float32, _float64 float64, _bool bool, _string string, _date time, _time time);] + +-- 639 // https://github.com/cznic/ql/issues/37 +BEGIN TRANSACTION; + CREATE TABLE artist (id int64, name string); + CREATE TABLE data_types (id int64, _uint int64, _uint8 int64, _uint16 + int64, _uint32 int64, _uint64 int64, _int int64, _int8 int64, + _int16 int64, _int32 int64, _int64 int64, _float32 float32, + _float64 float64, _bool bool, _string string, _date time, _time + time); +COMMIT; +SELECT * FROM __Table WHERE Name == "artist"; +|"Name", "Schema" +[artist CREATE TABLE artist (id int64, name string);] + +-- 640 // https://github.com/cznic/ql/issues/37 +BEGIN TRANSACTION; + CREATE TABLE artist (id int64, name string); + CREATE TABLE data_types (id int64, _uint int64, _uint8 int64, _uint16 + int64, _uint32 int64, _uint64 int64, _int int64, _int8 int64, + _int16 int64, _int32 int64, _int64 int64, _float32 float32, + _float64 float64, _bool bool, _string string, _date time, _time + time); +COMMIT; +SELECT * FROM __Column ORDER BY TableName, Ordinal; +|"TableName", "Ordinal", "Name", "Type" +[artist 1 id int64] +[artist 2 name string] +[data_types 1 id int64] +[data_types 2 _uint int64] +[data_types 3 _uint8 int64] +[data_types 4 _uint16 int64] +[data_types 5 _uint32 int64] +[data_types 6 _uint64 int64] +[data_types 7 _int int64] +[data_types 8 _int8 int64] +[data_types 9 _int16 int64] +[data_types 10 _int32 int64] +[data_types 11 _int64 int64] +[data_types 12 _float32 float32] +[data_types 13 _float64 float64] +[data_types 14 _bool bool] +[data_types 15 _string string] +[data_types 16 _date time] +[data_types 17 _time time] + +-- 641 // https://github.com/cznic/ql/issues/37 +BEGIN TRANSACTION; + CREATE TABLE artist (id int64, name string); + CREATE TABLE data_types (id int64, _uint int64, _uint8 int64, _uint16 + int64, _uint32 int64, _uint64 int64, _int int64, _int8 int64, + _int16 int64, _int32 int64, _int64 int64, _float32 float32, + _float64 float64, _bool bool, _string string, _date time, _time + time); +COMMIT; +SELECT * FROM __Column WHERE TableName == "artist" ORDER BY TableName, Ordinal; +|"TableName", "Ordinal", "Name", "Type" +[artist 1 id int64] +[artist 2 name string] + +-- 642 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, 6); + CREATE TABLE u (x int, y int, z int); + INSERT INTO u VALUES + (10, 20, 30), + (40, 50, 60); +COMMIT; +SELECT * FROM t, u WHERE u.y < 60 && t.k < 7; +|"t.i", "t.j", "t.k", "u.x", "u.y", "u.z" +[4 5 6 40 50 60] +[4 5 6 10 20 30] +[1 2 3 40 50 60] +[1 2 3 10 20 30] + +-- 643 // order -> xk used +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + CREATE INDEX xk ON t (k); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, 6); + CREATE TABLE u (x int, y int, z int); + INSERT INTO u VALUES + (10, 20, 30), + (40, 50, 60); +COMMIT; +SELECT * FROM t, u WHERE u.y < 60 && t.k < 7; +|"t.i", "t.j", "t.k", "u.x", "u.y", "u.z" +[1 2 3 40 50 60] +[1 2 3 10 20 30] +[4 5 6 40 50 60] +[4 5 6 10 20 30] + +-- 644 // order -> xy used +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, 6); + CREATE TABLE u (x int, y int, z int); + CREATE INDEX xy ON u (y); + INSERT INTO u VALUES + (10, 20, 30), + (40, 50, 60); +COMMIT; +SELECT * FROM t, u WHERE u.y < 60 && t.k < 7; +|"t.i", "t.j", "t.k", "u.x", "u.y", "u.z" +[4 5 6 10 20 30] +[4 5 6 40 50 60] +[1 2 3 10 20 30] +[1 2 3 40 50 60] + +-- 645 // order -> both xk and xy used +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + CREATE INDEX xk ON t (k); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, 6); + CREATE TABLE u (x int, y int, z int); + CREATE INDEX xy ON u (y); + INSERT INTO u VALUES + (10, 20, 30), + (40, 50, 60); +COMMIT; +SELECT * FROM t, u WHERE u.y < 60 && t.k < 7; +|"t.i", "t.j", "t.k", "u.x", "u.y", "u.z" +[1 2 3 10 20 30] +[1 2 3 40 50 60] +[4 5 6 10 20 30] +[4 5 6 40 50 60] + +-- 646 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM t OFFSET -1; // no rows -> not evaluated +|"i" + +-- 647 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM t OFFSET 0; // no rows -> not evaluated +|"i" + +-- 648 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM t OFFSET 1; // no rows -> not evaluated +|"i" + +-- 649 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() OFFSET -1; +||invalid .* -1 .*must.* non-negative + +-- 650 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() OFFSET 0; +|"i" +[42] +[24] + +-- 651 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() OFFSET 1; +|"i" +[24] + +-- 652 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() OFFSET 2; +|"i" + +-- 653 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT -1; // no rows -> not evaluated +|"i" + +-- 654 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 0; // no rows -> not evaluated +|"i" + +-- 655 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 1; // no rows -> not evaluated +|"i" + +-- 656 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT -1; +||invalid .* -1 .*must.* non-negative + +-- 657 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 0; +|"i" + +-- 658 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 1; +|"i" +[42] + +-- 659 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 2; +|"i" +[42] +[24] + +-- 660 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 3; +|"i" +[42] +[24] + +-- 661 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 0 OFFSET 0; +|"i" + +-- 662 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 0 OFFSET 1; +|"i" + +-- 663 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 0 OFFSET 2; +|"i" + +-- 664 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 0 OFFSET 3; +|"i" + +-- 665 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 1 OFFSET 0; +|"i" +[42] + +-- 666 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 1 OFFSET 1; +|"i" +[24] + +-- 667 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 1 OFFSET 2; +|"i" + +-- 668 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 1 OFFSET 3; +|"i" + +-- 669 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 2 OFFSET 0; +|"i" +[42] +[24] + +-- 670 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 2 OFFSET 1; +|"i" +[24] + +-- 671 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 2 OFFSET 2; +|"i" + +-- 672 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 2 OFFSET 3; +|"i" + +-- 673 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 3 OFFSET 0; +|"i" +[42] +[24] + +-- 674 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 3 OFFSET 1; +|"i" +[24] + +-- 675 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 3 OFFSET 2; +|"i" + +-- 676 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42), (24); +COMMIT; +SELECT * FROM t ORDER BY id() LIMIT 3 OFFSET 3; +|"i" + +-- 677 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3); + CREATE TABLE u (i int); + INSERT INTO u VALUES(10), (20), (30); +COMMIT; +SELECT * FROM + (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, + (SELECT * FROM u ORDER BY i) AS b, +ORDER BY a.i, b.i; +|"a.i", "b.i" +[2 10] +[2 20] +[2 30] +[3 10] +[3 20] +[3 30] + +-- 678 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3); + CREATE TABLE u (i int); + INSERT INTO u VALUES(10), (20), (30); +COMMIT; +SELECT * FROM + (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, + (SELECT * FROM u ORDER BY i OFFSET 1) AS b, +ORDER BY a.i, b.i; +|"a.i", "b.i" +[2 20] +[2 30] +[3 20] +[3 30] + +-- 679 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3); + CREATE TABLE u (i int); + INSERT INTO u VALUES(10), (20), (30); +COMMIT; +SELECT * FROM + (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, + (SELECT * FROM u ORDER BY i LIMIT 1) AS b, +ORDER BY a.i, b.i; +|"a.i", "b.i" +[2 10] +[3 10] + +-- 680 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3); + CREATE TABLE u (i int); + INSERT INTO u VALUES(10), (20), (30); +COMMIT; +SELECT * FROM + (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, + (SELECT * FROM u ORDER BY i LIMIT 1 OFFSET 1) AS b, +ORDER BY a.i, b.i; +|"a.i", "b.i" +[2 20] +[3 20] + +-- 681 // https://github.com/cznic/ql/issues/41 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3); + CREATE TABLE u (i int); + INSERT INTO u VALUES(10), (20), (30); +COMMIT; +SELECT * FROM + (SELECT * FROM t ORDER BY i LIMIT 2 OFFSET 1;) AS a, + (SELECT * FROM u ORDER BY i LIMIT 1 OFFSET 1) AS b, +ORDER BY a.i, b.i +LIMIT 1; +|"a.i", "b.i" +[2 20] + +-- 682 // https://github.com/cznic/ql/issues/42 +BEGIN TRANSACTION; + DROP TABLE IF EXISTS fibonacci; + CREATE TABLE fibonacci( + input int, + output int + ); +COMMIT; + +BEGIN TRANSACTION; + INSERT INTO fibonacci (input, output) VALUES (0, 0); + INSERT INTO fibonacci (input, output) VALUES (1, 1); + INSERT INTO fibonacci (input, output) VALUES (2, 1); + INSERT INTO fibonacci (input, output) VALUES (3, 2); + INSERT INTO fibonacci (input, output) VALUES (4, 3); + INSERT INTO fibonacci (input, output) VALUES (5, 5); + INSERT INTO fibonacci (input, output) VALUES (6, 8); + INSERT INTO fibonacci (input, output) VALUES (7, 13); + INSERT INTO fibonacci (input, output) VALUES (8, 21); + INSERT INTO fibonacci (input, output) VALUES (9, 34); +COMMIT; + +--' Should print 4. +SELECT count(1) AS total FROM fibonacci WHERE input >= 5 && input <= 7 OR input == 3; +|"total" +[4] + +-- 683 // https://github.com/cznic/ql/issues/42 +BEGIN TRANSACTION; + DROP TABLE IF EXISTS fibonacci; + CREATE TABLE fibonacci( + input int, + output int + ); +COMMIT; + +BEGIN TRANSACTION; + INSERT INTO fibonacci (input, output) VALUES (0, 0); + INSERT INTO fibonacci (input, output) VALUES (1, 1); + INSERT INTO fibonacci (input, output) VALUES (2, 1); + INSERT INTO fibonacci (input, output) VALUES (3, 2); + INSERT INTO fibonacci (input, output) VALUES (4, 3); + INSERT INTO fibonacci (input, output) VALUES (5, 5); + INSERT INTO fibonacci (input, output) VALUES (6, 8); + INSERT INTO fibonacci (input, output) VALUES (7, 13); + INSERT INTO fibonacci (input, output) VALUES (8, 21); + INSERT INTO fibonacci (input, output) VALUES (9, 34); +COMMIT; + +--' Should output (6, 8) (5, 5). +SELECT * FROM fibonacci WHERE input >= 5 && input <= 7 OR input == 3 ORDER BY input DESC LIMIT 2 OFFSET 1; +|"input", "output" +[6 8] +[5 5] + +-- 684 // https://github.com/cznic/ql/issues/42 +BEGIN TRANSACTION; + DROP TABLE IF EXISTS fibonacci; + CREATE TABLE fibonacci( + input int, + output int + ); +COMMIT; + +BEGIN TRANSACTION; + INSERT INTO fibonacci (input, output) VALUES (0, 0); + INSERT INTO fibonacci (input, output) VALUES (1, 1); + INSERT INTO fibonacci (input, output) VALUES (2, 1); + INSERT INTO fibonacci (input, output) VALUES (3, 2); + INSERT INTO fibonacci (input, output) VALUES (4, 3); + INSERT INTO fibonacci (input, output) VALUES (5, 5); + INSERT INTO fibonacci (input, output) VALUES (6, 8); + INSERT INTO fibonacci (input, output) VALUES (7, 13); + INSERT INTO fibonacci (input, output) VALUES (8, 21); + INSERT INTO fibonacci (input, output) VALUES (9, 34); + --' Let's delete 4 rows. + // Delete where input == 5, input == 6, input == 7 or input == 3 + DELETE FROM fibonacci WHERE input >= 5 && input <= 7 OR input == 3; +COMMIT; +SELECT * FROM fibonacci ORDER BY input; +|"input", "output" +[0 0] +[1 1] +[2 1] +[4 3] +[8 21] +[9 34] + +-- 685 // https://github.com/cznic/ql/issues/42 +BEGIN TRANSACTION; + DROP TABLE IF EXISTS fibonacci; + CREATE TABLE fibonacci( + input int, + output int + ); +COMMIT; + +BEGIN TRANSACTION; + INSERT INTO fibonacci (input, output) VALUES (0, 0); + INSERT INTO fibonacci (input, output) VALUES (1, 1); + INSERT INTO fibonacci (input, output) VALUES (2, 1); + INSERT INTO fibonacci (input, output) VALUES (3, 2); + INSERT INTO fibonacci (input, output) VALUES (4, 3); + INSERT INTO fibonacci (input, output) VALUES (5, 5); + INSERT INTO fibonacci (input, output) VALUES (6, 8); + INSERT INTO fibonacci (input, output) VALUES (7, 13); + INSERT INTO fibonacci (input, output) VALUES (8, 21); + INSERT INTO fibonacci (input, output) VALUES (9, 34); +COMMIT; +--' Let's delete 4 rows. +BEGIN TRANSACTION; + // Delete where input == 5, input == 6, input == 7 or input == 3 + DELETE FROM fibonacci WHERE input >= 5 && input <= 7 OR input == 3; +COMMIT; +SELECT * FROM fibonacci ORDER BY input; +|"input", "output" +[0 0] +[1 1] +[2 1] +[4 3] +[8 21] +[9 34] + +-- 686 // https://github.com/cznic/ql/issues/42 +BEGIN TRANSACTION; + DROP TABLE IF EXISTS fibonacci; + CREATE TABLE fibonacci( + input int, + output int + ); +COMMIT; + +BEGIN TRANSACTION; + INSERT INTO fibonacci (input, output) VALUES (0, 0); + INSERT INTO fibonacci (input, output) VALUES (1, 1); + INSERT INTO fibonacci (input, output) VALUES (2, 1); + INSERT INTO fibonacci (input, output) VALUES (3, 2); + INSERT INTO fibonacci (input, output) VALUES (4, 3); + INSERT INTO fibonacci (input, output) VALUES (5, 5); + INSERT INTO fibonacci (input, output) VALUES (6, 8); + INSERT INTO fibonacci (input, output) VALUES (7, 13); + INSERT INTO fibonacci (input, output) VALUES (8, 21); + INSERT INTO fibonacci (input, output) VALUES (9, 34); + --' Let's delete 4 rows. + // Delete where input == 5, input == 6, input == 7 or input == 3 + DELETE FROM fibonacci WHERE input >= 5 && input <= 7 OR input == 3; +COMMIT; +--' Try to count the rows we've just deleted, using the very same condition. Result is 1, should be 0. +SELECT count() AS total FROM fibonacci WHERE input >= 5 && input <= 7 OR input == 3; +|"total" +[0] + +-- 687 // https://github.com/cznic/ql/issues/42 +BEGIN TRANSACTION; + DROP TABLE IF EXISTS fibonacci; + CREATE TABLE fibonacci( + input int, + output int + ); +COMMIT; + +BEGIN TRANSACTION; + INSERT INTO fibonacci (input, output) VALUES (0, 0); + INSERT INTO fibonacci (input, output) VALUES (1, 1); + INSERT INTO fibonacci (input, output) VALUES (2, 1); + INSERT INTO fibonacci (input, output) VALUES (3, 2); + INSERT INTO fibonacci (input, output) VALUES (4, 3); + INSERT INTO fibonacci (input, output) VALUES (5, 5); + INSERT INTO fibonacci (input, output) VALUES (6, 8); + INSERT INTO fibonacci (input, output) VALUES (7, 13); + INSERT INTO fibonacci (input, output) VALUES (8, 21); + INSERT INTO fibonacci (input, output) VALUES (9, 34); +COMMIT; +BEGIN TRANSACTION; + --' Let's delete 4 rows. + // Delete where input == 5, input == 6, input == 7 or input == 3 + DELETE FROM fibonacci WHERE input >= 5 && input <= 7 OR input == 3; +COMMIT; +--' Try to count the rows we've just deleted, using the very same condition. Result is 1, should be 0. +SELECT count() AS total FROM fibonacci WHERE input >= 5 && input <= 7 OR input == 3; +|"total" +[0] + +-- 688 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1); + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" + +-- 689 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2); + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[2] + +-- 690 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2); + DELETE FROM t WHERE i == 2; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] + +-- 691 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3); + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[2] +[3] + +-- 692 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3); + DELETE FROM t WHERE i == 2; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[3] + +-- 693 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3); + DELETE FROM t WHERE i == 3; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[2] + +-- 694 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[2] +[3] +[4] + +-- 695 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 2; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[3] +[4] + +-- 696 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 3; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[2] +[4] + +-- 697 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 4; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[2] +[3] + +-- 698 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 1; + DELETE FROM t WHERE i == 2; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[3] +[4] + +-- 699 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 2; + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[3] +[4] + +-- 700 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 2; + DELETE FROM t WHERE i == 3; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[4] + +-- 701 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 3; + DELETE FROM t WHERE i == 2; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[4] + +-- 702 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 3; + DELETE FROM t WHERE i == 4; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[2] + +-- 703 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 4; + DELETE FROM t WHERE i == 3; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[2] + +-- 704 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 1; + DELETE FROM t WHERE i == 3; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[2] +[4] + +-- 705 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 3; + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[2] +[4] + +-- 706 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 1; + DELETE FROM t WHERE i == 4; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[2] +[3] + +-- 707 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 4; + DELETE FROM t WHERE i == 1; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[2] +[3] + +-- 708 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 2; + DELETE FROM t WHERE i == 4; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[3] + +-- 709 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(1), (2), (3), (4); + DELETE FROM t WHERE i == 4; + DELETE FROM t WHERE i == 2; +COMMIT; +SELECT * FROM t ORDER BY i; +|"i" +[1] +[3] + +-- 710 // https://github.com/cznic/ql/issues/43 +SELECT Name, Unique FROM __Index; +||expected .*Field + +-- 711 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); +COMMIT; +SELECT s FROM t; +|"s" +[foo] +[bar] + +-- 712 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); +COMMIT; +SELECT * FROM x; +|"x" +[bar] +[foo] + +-- 713 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (s); + CREATE INDEX x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); +COMMIT; +SELECT * FROM x; +||already + +-- 714 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX IF NOT EXISTS x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); +COMMIT; +SELECT * FROM x; +|"x" +[bar] +[foo] + +-- 715 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (s); + CREATE INDEX IF NOT EXISTS x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); +COMMIT; +SELECT * FROM x; +|"x" +[bar] +[foo] + +-- 716 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX IF NOT EXISTS x ON t (s); + CREATE INDEX IF NOT EXISTS x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); +COMMIT; +SELECT s FROM t WHERE s != "z"; +|"s" +[bar] +[foo] + +-- 717 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX IF NOT EXISTS x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); +COMMIT; +SELECT s FROM t WHERE s < "z"; // ordered -> index is used +|"s" +[bar] +[foo] + +-- 718 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX IF NOT EXISTS x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); + DROP INDEX x; +COMMIT; +SELECT s FROM t WHERE s < "z"; +|"s" +[foo] +[bar] + +-- 719 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX IF NOT EXISTS x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); + DROP INDEX x; + DROP INDEX x; +COMMIT; +SELECT s FROM t WHERE s < "z"; +||does not exist + +-- 720 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX IF NOT EXISTS x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); + DROP INDEX IF EXISTS x; + DROP INDEX x; +COMMIT; +SELECT s FROM t WHERE s < "z"; +||does not exist + +-- 721 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX IF NOT EXISTS x ON t (s); + INSERT INTO t VALUES (1, "bar"), (2, "foo"); + DROP INDEX x; + DROP INDEX IF EXISTS x; +COMMIT; +SELECT s FROM t WHERE s < "z"; +|"s" +[foo] +[bar] + +-- 722 +BEGIN TRANSACTION; + CREATE TABLE t (p string, c blob); + CREATE UNIQUE INDEX x ON t (p); + INSERT INTO t VALUES + ("empty", blob("")), + ; +COMMIT; +SELECT p, string(c) FROM t; +|"p", "" +[empty ] + +-- 723 +BEGIN TRANSACTION; + CREATE TABLE t (p string, c blob); + CREATE INDEX x ON t (p); + INSERT INTO t VALUES + ("empty", blob("")), + ; +COMMIT; +BEGIN TRANSACTION; + DELETE FROM t WHERE p == "empty"; +COMMIT; +SELECT p, string(c) FROM t; +|"p", "" + +-- 724 +BEGIN TRANSACTION; + CREATE TABLE t (p string, c blob); + CREATE UNIQUE INDEX x ON t (p); + INSERT INTO t VALUES + ("empty", blob("")), + ; +COMMIT; +BEGIN TRANSACTION; + DELETE FROM t WHERE p == "empty"; +COMMIT; +SELECT p, string(c) FROM t; +|"p", "" + +-- S 725 +BEGIN TRANSACTION; + UPDATE none SET + DepartmentID = DepartmentID+1000, + WHERE DepartmentID == 33; +COMMIT; +SELECT * FROM employee; +||table.*not.*exist + +-- S 726 +BEGIN TRANSACTION; + UPDATE employee SET + FirstName = "Williams" + WHERE DepartmentID == 33; +COMMIT; +SELECT * FROM employee; +||unknown.*FirstName + +-- S 727 +BEGIN TRANSACTION; + UPDATE employee SET + DepartmentID = DepartmentID+1000, + WHERE DepartmentID == 33; +COMMIT; +SELECT * FROM employee +ORDER BY LastName; +|"LastName", "DepartmentID" +[Heisenberg 1033] +[Jones 1033] +[Rafferty 31] +[Robinson 34] +[Smith 34] +[Williams ] + +-- 728 // https://github.com/cznic/ql/issues/49 +BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS t (username string, departname string, created time, detail_id int, height float64, avatar blob, is_man bool); + CREATE UNIQUE INDEX UQE_userinfo_username ON t (username); + INSERT INTO t (username, departname, created, detail_id, height, avatar, is_man) VALUES ( + "xiaolunwen", + "dev", + now(), + 1, + 1.78, + blob("012"), + true, + ); + DELETE FROM t WHERE id() IN (SELECT id() FROM t); +COMMIT; +SELECT * FROM t; +|"username", "departname", "created", "detail_id", "height", "avatar", "is_man" + +-- 729 // https://github.com/cznic/ql/issues/49 +BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS t (username string, departname string, created time, detail_id int, height float64, avatar blob, is_man bool); + CREATE UNIQUE INDEX UQE_userinfo_username ON t (username); + INSERT INTO t (username, departname, created, detail_id, height, avatar, is_man) VALUES ( + "xiaolunwen", + "dev", + now(), + 1, + 1.78, + __testBlob(256), + true, + ); + DELETE FROM t WHERE id() IN (SELECT id() FROM t); +COMMIT; +SELECT * FROM t; +|"username", "departname", "created", "detail_id", "height", "avatar", "is_man" + +-- 730 // https://github.com/cznic/ql/issues/49 +BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS t (username string, departname string, created time, detail_id int, height float64, avatar blob, is_man bool); + CREATE UNIQUE INDEX UQE_userinfo_username ON t (username); + INSERT INTO t (username, departname, created, detail_id, height, avatar, is_man) VALUES ( + "xiaolunwen", + "dev", + now(), + 1, + 1.78, + __testBlob(1<<16), + true, + ); + DELETE FROM t WHERE id() IN (SELECT id() FROM t); +COMMIT; +SELECT * FROM t; +|"username", "departname", "created", "detail_id", "height", "avatar", "is_man" + +-- 731 // https://github.com/cznic/ql/issues/49 +BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS t (username string, departname string, created time, detail_id int, height float64, avatar blob, is_man bool); + CREATE UNIQUE INDEX UQE_userinfo_username ON t (username); + INSERT INTO t (username, departname, created, detail_id, height, avatar, is_man) VALUES ( + "xiaolunwen", + "dev", + now(), + 1, + 1.78, + __testBlob(1<<20), + true, + ); + DELETE FROM t WHERE id() IN (SELECT id() FROM t); +COMMIT; +SELECT * FROM t; +|"username", "departname", "created", "detail_id", "height", "avatar", "is_man" + +-- 732 // https://github.com/cznic/ql/issues/49 +BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS t (username string, departname string, created time, detail_id int, height float64, avatar blob, is_man bool); + CREATE UNIQUE INDEX UQE_userinfo_username ON t (username); + INSERT INTO t (username, departname, created, detail_id, height, avatar, is_man) VALUES ( + "xiaolunwen", + "dev", + now(), + 1, + 1.78, + __testBlob(1<<20), + true, + ), ( + "2xiaolunwen", + "2dev", + now(), + 2, + 2.78, + __testBlob(1<<21), + true, + ); + DELETE FROM t WHERE id() IN (SELECT id() FROM t WHERE username == "xiaolunwen"); //TODO simplify, also everywhere else +COMMIT; +SELECT id() IN (SELECT id() FROM t WHERE username == "2xiaolunwen"), username == "2xiaolunwen", len(string(avatar)) == 1<<21 FROM t; +|"", "", "" +[true true true] + +-- 733 // https://github.com/cznic/ql/issues/49 +BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS t (username string, departname string, created time, detail_id int, height float64, avatar blob, is_man bool); + CREATE UNIQUE INDEX UQE_userinfo_username ON t (username); + INSERT INTO t (username, departname, created, detail_id, height, avatar, is_man) VALUES ( + "xiaolunwen", + "dev", + now(), + 1, + 1.78, + __testBlob(1<<20), + true, + ), ( + "2xiaolunwen", + "2dev", + now(), + 2, + 2.78, + __testBlob(1<<21), + true, + ); + DELETE FROM t WHERE id() IN (SELECT id() FROM t WHERE username == "2xiaolunwen"); +COMMIT; +SELECT id() IN (SELECT id() FROM t WHERE username == "xiaolunwen"), username == "xiaolunwen", len(string(avatar)) == 1<<20 FROM t; +|"", "", "" +[true true true] + +-- 734 // https://github.com/cznic/ql/issues/51 +BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS no_id_user (user string, remain int, total int); + CREATE UNIQUE INDEX UQE_no_id_user_user ON no_id_user (user); + DELETE FROM no_id_user WHERE user == "xlw"; + INSERT INTO no_id_user (user, remain, total) VALUES ("xlw", 20, 100); +COMMIT; +SELECT user, remain, total FROM no_id_user WHERE user == "xlw" LIMIT 1; +|"user", "remain", "total" +[xlw 20 100] + +-- 735 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (1), (2), (3), (4), (5), (6); +COMMIT; +SELECT * FROM t WHERE id() < 4; // reverse order -> no index used +|"i" +[3] +[2] +[1] + +-- 736 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (1), (2), (3), (4), (5), (6); +COMMIT; +SELECT * FROM t WHERE i < 4 ; // ordered -> index is used +|"i" +[1] +[2] +[3] + +-- 737 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (1), (2), (3), (4), (5), (6); +COMMIT; +SELECT * FROM t WHERE i <= 4; // ordered -> index is used +|"i" +[1] +[2] +[3] +[4] + +-- 738 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (1), (2), (3), (4), (5), (6); +COMMIT; +SELECT * FROM t WHERE i == 4; +|"i" +[4] + +-- 739 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (1), (2), (3), (4), (5), (6); +COMMIT; +SELECT * FROM t WHERE i >= 4; // ordered -> index is used +|"i" +[4] +[5] +[6] + +-- 740 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + INSERT INTO t VALUES (1), (2), (3), (4), (5), (6); +COMMIT; +SELECT * FROM t WHERE i > 4; +|"i" +[5] +[6] + +-- 741 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i); + CREATE TABLE u (i int); + CREATE INDEX y ON u (i); + INSERT INTO t VALUES (1), (2), (3), (4), (5), (6); + INSERT INTO u VALUES (10), (20), (30), (40), (50), (60); +COMMIT; +SELECT * FROM + (SELECT i FROM t WHERE i < 4) AS t, + (SELECT * FROM u WHERE i < 40) AS u; // ordered -> both indices are used +|"t.i", "u.i" +[1 10] +[1 20] +[1 30] +[2 10] +[2 20] +[2 30] +[3 10] +[3 20] +[3 30] + +-- 742 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (NULL), + ; +COMMIT; +SELECT max(t) as T FROM t; +|"T" +[] + +-- 743 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (NULL), + (NULL), + ; +COMMIT; +SELECT max(t) as T FROM t; +|"T" +[] + +-- 744 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (NULL), + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:11")), + ; +COMMIT; +SELECT max(t) as T FROM t; +|"T" +[2014-08-08 14:05:11 +0000 UTC] + +-- 745 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:11")), + (NULL), + ; +COMMIT; +SELECT max(t) as T FROM t; +|"T" +[2014-08-08 14:05:11 +0000 UTC] + +-- 746 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:11")), + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:12")), + ; +COMMIT; +SELECT max(t) as T FROM t; +|"T" +[2014-08-08 14:05:12 +0000 UTC] + +-- 747 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:12")), + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:11")), + ; +COMMIT; +SELECT max(t) as T FROM t; +|"T" +[2014-08-08 14:05:12 +0000 UTC] + +-- 748 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (NULL), + ; +COMMIT; +SELECT min(t) as T FROM t; +|"T" +[] + +-- 749 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (NULL), + (NULL), + ; +COMMIT; +SELECT min(t) as T FROM t; +|"T" +[] + +-- 750 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (NULL), + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:11")), + ; +COMMIT; +SELECT min(t) as T FROM t; +|"T" +[2014-08-08 14:05:11 +0000 UTC] + +-- 751 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:11")), + (NULL), + ; +COMMIT; +SELECT min(t) as T FROM t; +|"T" +[2014-08-08 14:05:11 +0000 UTC] + +-- 752 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:11")), + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:12")), + ; +COMMIT; +SELECT min(t) as T FROM t; +|"T" +[2014-08-08 14:05:11 +0000 UTC] + +-- 753 // https://github.com/cznic/ql/pull/65 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:12")), + (parseTime("2006-01-02 15:04:05", "2014-08-08 14:05:11")), + ; +COMMIT; +SELECT min(t) as T FROM t; +|"T" +[2014-08-08 14:05:11 +0000 UTC] + +-- 754 // https://github.com/cznic/ql/issues/68 +BEGIN TRANSACTION; + CREATE TABLE department (Name string); + INSERT INTO department (Name) VALUES ("small"), ("large"), ("medium"); + SELECT * FROM department; + ALTER TABLE department ADD score float; + SELECT * from department; + UPDATE department SET score=0 WHERE Name=="small"; +COMMIT; +SELECT * FROM department ORDER BY Name; +|"Name", "score" +[large ] +[medium ] +[small 0] + +-- 755 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE s LIKE "foo" +ORDER BY id(); +|"", "s" +[1 seafood] +[2 A fool on the hill] +[5 foobar] + +-- 756 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE !(s LIKE "foo") +ORDER BY id(); +|"", "s" +[4 barbaz] + +-- 757 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE s LIKE "foo" IS NULL +ORDER BY id(); +|"", "s" +[3 ] + +-- 758 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE s LIKE "foo" IS NOT NULL +ORDER BY id(); +|"", "s" +[1 seafood] +[2 A fool on the hill] +[4 barbaz] +[5 foobar] + +-- 759 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE s LIKE "bar" +ORDER BY id(); +|"", "s" +[4 barbaz] +[5 foobar] + +-- 760 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE s LIKE "^bar" +ORDER BY id(); +|"", "s" +[4 barbaz] + +-- 761 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE s LIKE "bar$" +ORDER BY id(); +|"", "s" +[5 foobar] + +-- 762 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE s LIKE "bar"+"$" +ORDER BY id(); +|"", "s" +[5 foobar] + +-- 763 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE s+"qux" LIKE "qux"+"$" +ORDER BY id(); +|"", "s" +[1 seafood] +[2 A fool on the hill] +[4 barbaz] +[5 foobar] + +-- 764 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES + ("seafood"), + ("A fool on the hill"), + (NULL), + ("barbaz"), + ("foobar"), + ; +COMMIT; +SELECT id(), s +FROM t +WHERE s+"quxx" LIKE "qux"+"$" +ORDER BY id(); +|"", "s" + +-- 765 // https://github.com/cznic/ql/issues/75 +BEGIN TRANSACTION; + CREATE TABLE foo (i int); + INSERT INTO foo VALUES (10), (20); + CREATE TABLE bar (fooID int, s string); + INSERT INTO bar SELECT id(), "ten" FROM foo WHERE i == 10; + INSERT INTO bar SELECT id(), "twenty" FROM foo WHERE i == 20; +COMMIT; +SELECT * +FROM + (SELECT id() AS ID, i FROM foo) AS foo, + bar +WHERE bar.fooID == foo.ID +ORDER BY foo.ID; +|"foo.ID", "foo.i", "bar.fooID", "bar.s" +[1 10 1 ten] +[2 20 2 twenty] + +-- 766 // https://github.com/cznic/ql/issues/75 +BEGIN TRANSACTION; + CREATE TABLE foo (i int); + INSERT INTO foo VALUES (10), (20); + CREATE TABLE bar (fooID int, s string); + INSERT INTO bar SELECT id(), "ten" FROM foo WHERE i == 10; + INSERT INTO bar SELECT id(), "twenty" FROM foo WHERE i == 20; +COMMIT; +SELECT * +FROM foo, bar +WHERE bar.fooID == id(foo) +ORDER BY id(foo); +|"foo.i", "bar.fooID", "bar.s" +[10 1 ten] +[20 2 twenty] + +-- 767 // https://github.com/cznic/ql/issues/81 +BEGIN TRANSACTION; + CREATE TABLE t (name string, mail string); + INSERT INTO t VALUES + ("a", "foo@example.com"), + ("b", "bar@example.com"), + ("c", "baz@example.com"), + ("d", "foo@example.com"), + ("e", "bar@example.com"), + ("f", "baz@example.com"), + ; +COMMIT; +SELECT * +FROM t +WHERE name == "b" AND mail == "bar@example.com"; +|"name", "mail" +[b bar@example.com] + +-- 768 // https://github.com/cznic/ql/issues/81 +BEGIN TRANSACTION; + CREATE TABLE t (name string, mail string); + INSERT INTO t VALUES + ("a", "foo@example.com"), + ("b", "bar@example.com"), + ("c", "baz@example.com"), + ("d", "foo@example.com"), + ("e", "bar@example.com"), + ("f", "baz@example.com"), + ; +COMMIT; +SELECT * +FROM t +WHERE name == "b" and mail == "bar@example.com"; +|"name", "mail" +[b bar@example.com] + +-- 769 // https://github.com/cznic/ql/issues/81 +BEGIN TRANSACTION; + CREATE TABLE t (name string, mail string); + INSERT INTO t VALUES + ("a", "foo@example.com"), + ("b", "bar@example.com"), + ("c", "baz@example.com"), + ("d", "foo@example.com"), + ("e", "bar@example.com"), + ("f", "baz@example.com"), + ; +COMMIT; +SELECT * +FROM t +WHERE name == "b" OR mail == "bar@example.com" +ORDER BY name; +|"name", "mail" +[b bar@example.com] +[e bar@example.com] + +-- 770 // https://github.com/cznic/ql/issues/81 +BEGIN TRANSACTION; + CREATE TABLE t (name string, mail string); + INSERT INTO t VALUES + ("a", "foo@example.com"), + ("b", "bar@example.com"), + ("c", "baz@example.com"), + ("d", "foo@example.com"), + ("e", "bar@example.com"), + ("f", "baz@example.com"), + ; +COMMIT; +SELECT * +FROM t +WHERE name == "b" or mail == "bar@example.com" +ORDER BY name; +|"name", "mail" +[b bar@example.com] +[e bar@example.com] + +-- 771 // https://github.com/cznic/ql/issues/72 +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB + SELECT id() FROM tableA WHERE i&1 == 0; +COMMIT; +SELECT id(), i FROM tableA WHERE id() IN (SELECT idA FROM tableB) ORDER BY id(); +|"", "i" +[2 12] +[4 14] +[6 16] + +-- 772 // https://github.com/cznic/ql/issues/72 +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB + SELECT id() FROM tableA WHERE i&1 == 0; +COMMIT; +SELECT id(), i FROM tableA WHERE id() NOT IN (SELECT idA FROM tableB) ORDER BY id(); +|"", "i" +[1 11] +[3 13] +[5 15] + +-- 773 // https://github.com/cznic/ql/issues/72 +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB + SELECT id() FROM tableA WHERE i&1 == 0; + DELETE FROM tableA WHERE id() IN (SELECT idA FROM tableB); +COMMIT; +SELECT id(), i FROM tableA ORDER BY id(); +|"", "i" +[1 11] +[3 13] +[5 15] + +-- 774 // https://github.com/cznic/ql/issues/72 +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB + SELECT id() FROM tableA WHERE i&1 == 0; + DELETE FROM tableA WHERE id() NOT IN (SELECT idA FROM tableB); +COMMIT; +SELECT id(), i FROM tableA ORDER BY id(); +|"", "i" +[2 12] +[4 14] +[6 16] + +-- 775 // https://github.com/cznic/ql/issues/72, coerce +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB + SELECT id() FROM tableA WHERE i&1 == 0; + DELETE FROM tableA WHERE 2 IN (SELECT idA FROM tableB); +COMMIT; +SELECT id(), i FROM tableA ORDER BY id(); +|"", "i" + +-- 776 // https://github.com/cznic/ql/issues/72, coerce +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB + SELECT id() FROM tableA WHERE i&1 == 0; + DELETE FROM tableA WHERE 2 NOT IN (SELECT idA FROM tableB); +COMMIT; +SELECT id(), i FROM tableA ORDER BY id(); +|"", "i" +[1 11] +[2 12] +[3 13] +[4 14] +[5 15] +[6 16] + +-- 777 // https://github.com/cznic/ql/issues/72, different types have zero set intersetion. +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB + SELECT id() FROM tableA WHERE i&1 == 0; + DELETE FROM tableA WHERE 3.14 IN (SELECT idA FROM tableB); +COMMIT; +SELECT id(), i FROM tableA ORDER BY id(); +|"", "i" +[1 11] +[2 12] +[3 13] +[4 14] +[5 15] +[6 16] + +-- 778 // https://github.com/cznic/ql/issues/72, different have zero set intersection but NOT makes the result true. +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB + SELECT id() FROM tableA WHERE i&1 == 0; + DELETE FROM tableA WHERE 3.14 NOT IN (SELECT idA FROM tableB); +COMMIT; +SELECT id(), i FROM tableA ORDER BY id(); +|"", "i" + +-- 779 // https://github.com/cznic/ql/issues/72, invalid field type +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA time); + INSERT INTO tableB + SELECT now() FROM tableA WHERE i&1 == 0; + DELETE FROM tableA WHERE 3.14 NOT IN (SELECT idA FROM tableB); +COMMIT; +SELECT id(), i FROM tableA ORDER BY id(); +||invalid field type + +-- 780 // https://github.com/cznic/ql/issues/72, too many fields +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int, name string); + INSERT INTO tableB + SELECT id(), "foo" FROM tableA WHERE i&1 == 0; + DELETE FROM tableA WHERE id() NOT IN (SELECT * FROM tableB); +COMMIT; +SELECT id(), i FROM tableA ORDER BY id(); +||mismatched field count + +-- 781 // https://github.com/cznic/ql/issues/72, some NULL +BEGIN TRANSACTION; + DROP TABLE IF EXISTS tableA; + DROP TABLE IF EXISTS tableB; +COMMIT; +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB VALUES(NULL); + INSERT INTO tableB + SELECT id() FROM tableA WHERE i&1 == 0; +COMMIT; +SELECT i FROM tableA WHERE id() IN (SELECT idA from tableB) ORDER BY id(); +|"i" +[12] +[14] +[16] + +-- 782 // https://github.com/cznic/ql/issues/72, all NULL +BEGIN TRANSACTION; + DROP TABLE IF EXISTS tableA; + DROP TABLE IF EXISTS tableB; +COMMIT; +BEGIN TRANSACTION; + CREATE TABLE tableA (i int); + INSERT INTO tableA VALUES + (11), + (12), + (13), + (14), + (15), + (16), + ; + CREATE TABLE tableB (idA int); + INSERT INTO tableB VALUES(NULL); +COMMIT; +SELECT i FROM tableA WHERE id() IN (SELECT idA from tableB) ORDER BY id(); +|"i" + +-- 783 // https://github.com/cznic/ql/issues/84 +BEGIN TRANSACTION; + CREATE TABLE testA ( + comment string, + data blob, + ); +INSERT INTO testA (comment) VALUES ("c1"); +UPDATE testA SET data = blob("newVal"); +COMMIT; +SELECT * FROM testA; +|"comment", "data" +[c1 [110 101 119 86 97 108]] + +-- 784 // https://github.com/cznic/ql/issues/84 +BEGIN TRANSACTION; + CREATE TABLE testA ( + comment string, + data blob, + ); +INSERT INTO testA (comment) VALUES ("c1"); +UPDATE testA SET data = __testBlob(257); +COMMIT; +SELECT * FROM testA; +|"comment", "data" +[c1 [209 231 244 253 191 74 169 85 3 88 111 250 130 24 50 218 91 40 161 60 32 53 58 129 75 81 71 109 70 211 146 67 107 65 150 142 179 2 173 53 73 229 68 154 46 108 47 91 179 98 107 202 157 189 137 4 47 39 93 235 58 112 186 143 68 85 217 33 155 218 180 143 27 76 155 226 205 31 187 12 68 33 75 110 208 42 99 61 223 170 228 184 243 241 64 39 174 64 19 129 203 84 254 78 102 59 16 104 151 21 201 4 117 20 99 125 162 19 201 211 171 71 26 173 37 52 16 115 143 113 128 206 85 192 126 252 146 224 184 146 101 35 198 231 35 236 189 114 184 92 58 124 128 162 106 95 241 186 172 196 31 138 44 178 168 127 69 116 225 27 53 171 157 185 48 205 167 150 77 69 129 86 72 117 129 121 62 224 186 31 116 4 196 103 206 63 185 236 75 172 217 51 223 26 195 127 79 72 199 160 103 92 192 202 67 17 99 200 111 174 71 24 64 119 113 178 105 44 12 25 70 6 69 173 90 100 171 122 155 220 185 99 41 101 190 142 44 217 102 93 63 225 218 239 167 40 254]] + +-- 785 // https://github.com/cznic/ql/issues/84 +BEGIN TRANSACTION; + CREATE TABLE testA ( + comment string, + data blob, + ); +INSERT INTO testA (comment) VALUES ("c1"), ("c2"); +UPDATE testA SET data = blob("newVal") WHERE comment == "c1"; +COMMIT; +SELECT * FROM testA ORDER BY comment; +|"comment", "data" +[c1 [110 101 119 86 97 108]] +[c2 ] + +-- 786 // https://github.com/cznic/ql/issues/84 +BEGIN TRANSACTION; + CREATE TABLE testA ( + comment string, + data blob, + ); +INSERT INTO testA (comment) VALUES ("c1"), ("c2"); +UPDATE testA SET data = blob("newVal") WHERE comment == "c2"; +COMMIT; +SELECT * FROM testA ORDER BY comment; +|"comment", "data" +[c1 ] +[c2 [110 101 119 86 97 108]] + +-- 787 // https://github.com/cznic/ql/issues/84 +BEGIN TRANSACTION; + CREATE TABLE testA ( + comment string, + data blob, + ); +INSERT INTO testA (comment) VALUES ("c1"), ("c2"); +UPDATE testA SET data = blob("newVal"); +COMMIT; +SELECT * FROM testA ORDER BY comment; +|"comment", "data" +[c1 [110 101 119 86 97 108]] +[c2 [110 101 119 86 97 108]] + +-- 788 // https://github.com/cznic/ql/issues/84 +BEGIN TRANSACTION; + CREATE TABLE testA ( + comment string, + data blob, + ); +INSERT INTO testA (comment) VALUES ("c1"), ("c2"); +UPDATE testA SET data = __testBlob(257) WHERE comment == "c1"; +COMMIT; +SELECT * FROM testA ORDER BY comment; +|"comment", "data" +[c1 [209 231 244 253 191 74 169 85 3 88 111 250 130 24 50 218 91 40 161 60 32 53 58 129 75 81 71 109 70 211 146 67 107 65 150 142 179 2 173 53 73 229 68 154 46 108 47 91 179 98 107 202 157 189 137 4 47 39 93 235 58 112 186 143 68 85 217 33 155 218 180 143 27 76 155 226 205 31 187 12 68 33 75 110 208 42 99 61 223 170 228 184 243 241 64 39 174 64 19 129 203 84 254 78 102 59 16 104 151 21 201 4 117 20 99 125 162 19 201 211 171 71 26 173 37 52 16 115 143 113 128 206 85 192 126 252 146 224 184 146 101 35 198 231 35 236 189 114 184 92 58 124 128 162 106 95 241 186 172 196 31 138 44 178 168 127 69 116 225 27 53 171 157 185 48 205 167 150 77 69 129 86 72 117 129 121 62 224 186 31 116 4 196 103 206 63 185 236 75 172 217 51 223 26 195 127 79 72 199 160 103 92 192 202 67 17 99 200 111 174 71 24 64 119 113 178 105 44 12 25 70 6 69 173 90 100 171 122 155 220 185 99 41 101 190 142 44 217 102 93 63 225 218 239 167 40 254]] +[c2 ] + +-- 789 // https://github.com/cznic/ql/issues/84 +BEGIN TRANSACTION; + CREATE TABLE testA ( + comment string, + data blob, + ); +INSERT INTO testA (comment) VALUES ("c1"), ("c2"); +UPDATE testA SET data = __testBlob(257) WHERE comment == "c2"; +COMMIT; +SELECT * FROM testA ORDER BY comment; +|"comment", "data" +[c1 ] +[c2 [209 231 244 253 191 74 169 85 3 88 111 250 130 24 50 218 91 40 161 60 32 53 58 129 75 81 71 109 70 211 146 67 107 65 150 142 179 2 173 53 73 229 68 154 46 108 47 91 179 98 107 202 157 189 137 4 47 39 93 235 58 112 186 143 68 85 217 33 155 218 180 143 27 76 155 226 205 31 187 12 68 33 75 110 208 42 99 61 223 170 228 184 243 241 64 39 174 64 19 129 203 84 254 78 102 59 16 104 151 21 201 4 117 20 99 125 162 19 201 211 171 71 26 173 37 52 16 115 143 113 128 206 85 192 126 252 146 224 184 146 101 35 198 231 35 236 189 114 184 92 58 124 128 162 106 95 241 186 172 196 31 138 44 178 168 127 69 116 225 27 53 171 157 185 48 205 167 150 77 69 129 86 72 117 129 121 62 224 186 31 116 4 196 103 206 63 185 236 75 172 217 51 223 26 195 127 79 72 199 160 103 92 192 202 67 17 99 200 111 174 71 24 64 119 113 178 105 44 12 25 70 6 69 173 90 100 171 122 155 220 185 99 41 101 190 142 44 217 102 93 63 225 218 239 167 40 254]] + +-- 790 // https://github.com/cznic/ql/issues/84 +BEGIN TRANSACTION; + CREATE TABLE testA ( + comment string, + data blob, + ); +INSERT INTO testA (comment) VALUES ("c1"), ("c2"); +UPDATE testA SET data = __testBlob(257); +COMMIT; +SELECT * FROM testA ORDER BY comment; +|"comment", "data" +[c1 [209 231 244 253 191 74 169 85 3 88 111 250 130 24 50 218 91 40 161 60 32 53 58 129 75 81 71 109 70 211 146 67 107 65 150 142 179 2 173 53 73 229 68 154 46 108 47 91 179 98 107 202 157 189 137 4 47 39 93 235 58 112 186 143 68 85 217 33 155 218 180 143 27 76 155 226 205 31 187 12 68 33 75 110 208 42 99 61 223 170 228 184 243 241 64 39 174 64 19 129 203 84 254 78 102 59 16 104 151 21 201 4 117 20 99 125 162 19 201 211 171 71 26 173 37 52 16 115 143 113 128 206 85 192 126 252 146 224 184 146 101 35 198 231 35 236 189 114 184 92 58 124 128 162 106 95 241 186 172 196 31 138 44 178 168 127 69 116 225 27 53 171 157 185 48 205 167 150 77 69 129 86 72 117 129 121 62 224 186 31 116 4 196 103 206 63 185 236 75 172 217 51 223 26 195 127 79 72 199 160 103 92 192 202 67 17 99 200 111 174 71 24 64 119 113 178 105 44 12 25 70 6 69 173 90 100 171 122 155 220 185 99 41 101 190 142 44 217 102 93 63 225 218 239 167 40 254]] +[c2 [209 231 244 253 191 74 169 85 3 88 111 250 130 24 50 218 91 40 161 60 32 53 58 129 75 81 71 109 70 211 146 67 107 65 150 142 179 2 173 53 73 229 68 154 46 108 47 91 179 98 107 202 157 189 137 4 47 39 93 235 58 112 186 143 68 85 217 33 155 218 180 143 27 76 155 226 205 31 187 12 68 33 75 110 208 42 99 61 223 170 228 184 243 241 64 39 174 64 19 129 203 84 254 78 102 59 16 104 151 21 201 4 117 20 99 125 162 19 201 211 171 71 26 173 37 52 16 115 143 113 128 206 85 192 126 252 146 224 184 146 101 35 198 231 35 236 189 114 184 92 58 124 128 162 106 95 241 186 172 196 31 138 44 178 168 127 69 116 225 27 53 171 157 185 48 205 167 150 77 69 129 86 72 117 129 121 62 224 186 31 116 4 196 103 206 63 185 236 75 172 217 51 223 26 195 127 79 72 199 160 103 92 192 202 67 17 99 200 111 174 71 24 64 119 113 178 105 44 12 25 70 6 69 173 90 100 171 122 155 220 185 99 41 101 190 142 44 217 102 93 63 225 218 239 167 40 254]] + +-- 791 +BEGIN TRANSACTION; + CREATE TABLE t (c float32); + INSERT INTO t VALUES (43.2); +COMMIT; +SELECT formatFloat(c) FROM t; +|"" +[43.2] + +-- 792 +BEGIN TRANSACTION; + CREATE TABLE t (c float64); + INSERT INTO t VALUES (43.2); +COMMIT; +SELECT formatFloat(c) FROM t; +|"" +[43.2] + +-- 793 +BEGIN TRANSACTION; + CREATE TABLE t (c float64); + INSERT INTO t VALUES (43.2); +COMMIT; +SELECT formatFloat(c, 'b') FROM t; +|"" +[6079859496950170p-47] + +-- 794 +BEGIN TRANSACTION; + CREATE TABLE t (c float64); + INSERT INTO t VALUES (43.2); +COMMIT; +SELECT formatFloat(c, 'e', 5) FROM t; +|"" +[4.32000e+01] + +-- 795 +BEGIN TRANSACTION; + CREATE TABLE t (c float64); + INSERT INTO t VALUES (43.2); +COMMIT; +SELECT formatFloat(c, 'b', 7, 32) FROM t; +|"" +[11324621p-18] + +-- 796 +BEGIN TRANSACTION; + CREATE TABLE t (c int32); + INSERT INTO t VALUES (-42); +COMMIT; +SELECT formatInt(c) FROM t; +|"" +[-42] + +-- 797 +BEGIN TRANSACTION; + CREATE TABLE t (c int64); + INSERT INTO t VALUES (-42); +COMMIT; +SELECT formatInt(c) FROM t; +|"" +[-42] + +-- 798 +BEGIN TRANSACTION; + CREATE TABLE t (c int64); + INSERT INTO t VALUES (-42); +COMMIT; +SELECT formatInt(c, 18) FROM t; +|"" +[-26] + +-- 799 +BEGIN TRANSACTION; + CREATE TABLE t (c uint32); + INSERT INTO t VALUES (42); +COMMIT; +SELECT formatInt(c) FROM t; +|"" +[42] + +-- 800 +BEGIN TRANSACTION; + CREATE TABLE t (c uint64); + INSERT INTO t VALUES (42); +COMMIT; +SELECT formatInt(c) FROM t; +|"" +[42] + +-- 801 +BEGIN TRANSACTION; + CREATE TABLE t (c uint64); + INSERT INTO t VALUES (42); +COMMIT; +SELECT formatInt(c, 18) FROM t; +|"" +[26] + +-- 802 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + INSERT INTO t VALUES (-42); +COMMIT; +SELECT formatInt(c, 18) FROM t; +|"" +[-26] + +-- 803 +BEGIN TRANSACTION; + CREATE TABLE t (c uint); + INSERT INTO t VALUES (42); +COMMIT; +SELECT formatInt(c, 18) FROM t; +|"" +[26] + +-- 804 +BEGIN TRANSACTION; + CREATE TABLE t (c int8); + INSERT INTO t VALUES (-42); +COMMIT; +SELECT formatInt(c, 18) FROM t; +|"" +[-26] + +-- 805 +BEGIN TRANSACTION; + CREATE TABLE t (c uint8); + INSERT INTO t VALUES (42); +COMMIT; +SELECT formatInt(c, 18) FROM t; +|"" +[26] + +-- 806 +BEGIN TRANSACTION; + CREATE TABLE t (c int16); + INSERT INTO t VALUES (-42); +COMMIT; +SELECT formatInt(c, 18) FROM t; +|"" +[-26] + +-- 807 +BEGIN TRANSACTION; + CREATE TABLE t (c uint16); + INSERT INTO t VALUES (42); +COMMIT; +SELECT formatInt(c, 18) FROM t; +|"" +[26] + +-- 808 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); + ALTER TABLE t ADD b blob; +COMMIT; +SELECT * FROM t; +|"i", "b" +[42 ] + +-- 809 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); + ALTER TABLE t ADD b blob; + UPDATE t b = blob("a"); +COMMIT; +SELECT * FROM t; +|"i", "b" +[42 [97]] + +-- 810 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + DROP TABLE __Column2; +COMMIT; +||system table + +-- 811 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE __Column2 (i int); +COMMIT; +||system table + +-- 812 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + UPDATE __Column2 SET i = 42; +COMMIT; +||system table + +-- 813 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE INDEX __Column2Default ON __Column2(DefaultExpr); +COMMIT; +||system table + +-- 814 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM __Column2; +||does not exist + +-- 815 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int DEFAULT 42); +COMMIT; +SELECT * FROM __Column2; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t i false 42] + +-- 816 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int NOT NULL); +COMMIT; +SELECT * FROM __Column2; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t i true ] + +-- 817 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int NOT NULL DEFAULT 43); +COMMIT; +SELECT * FROM __Column2; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t i true 43] + +-- 818 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int i > 44); +COMMIT; +SELECT * FROM __Column2; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t i false i > 44 ] + +-- 819 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int i > 45 DEFAULT 46); +COMMIT; +SELECT * FROM __Column2; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t i false i > 45 46] + +-- 820 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + ALTER TABLE t ADD s string; +COMMIT; +SELECT * FROM __Column2; +||does not exist + +-- 821 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t ADD s string; +COMMIT; +SELECT * FROM __Column2; +||does not exist + +-- 822 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + ALTER TABLE t ADD s string DEFAULT "foo"; +COMMIT; +SELECT * FROM __Column2; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t s false "foo"] + +-- 823 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t ADD s string DEFAULT "foo"; +COMMIT; +SELECT * FROM __Column2; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t s false "foo"] + +-- 824 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + ALTER TABLE t ADD s string NOT NULL; +COMMIT; +SELECT * FROM __Column2; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t s true ] + +-- 825 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t ADD s string NOT NULL; +COMMIT; +SELECT * FROM __Column2; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t s true ] + +-- 826 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); + ALTER TABLE t ADD s string NOT NULL; +COMMIT; +SELECT * FROM __Column2; +||existing data + +-- 827 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t ADD s string NOT NULL; +COMMIT; +SELECT * FROM __Column2; +||existing data + +-- 828 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); + ALTER TABLE t ADD s string s > ""; +COMMIT; +SELECT * FROM __Column2; +||existing data + +-- 829 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES(42); + ALTER TABLE t ADD s string s > ""; +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t ADD s string s > ""; +COMMIT; +SELECT * FROM __Column2; +||existing data + +-- 830 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int DEFAULT 42, s string DEFAULT 43); +COMMIT; +SELECT * FROM __Column2 ORDER BY Name; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t i false 42] +[t s false 43] + +-- 831 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int DEFAULT 42, s string DEFAULT 43); + ALTER TABLE t DROP COLUMN s; +COMMIT; +SELECT * FROM __Column2 ORDER BY Name; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t i false 42] + +-- 832 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int DEFAULT 42, s string DEFAULT 43); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t DROP COLUMN s; +COMMIT; +SELECT * FROM __Column2 ORDER BY Name; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t i false 42] + +-- 833 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int DEFAULT 42, s string DEFAULT 43); + ALTER TABLE t DROP COLUMN i; +COMMIT; +SELECT * FROM __Column2 ORDER BY Name; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t s false 43] + +-- 834 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int DEFAULT 42, s string DEFAULT 43); +COMMIT; +BEGIN TRANSACTION; + ALTER TABLE t DROP COLUMN i; +COMMIT; +SELECT * FROM __Column2 ORDER BY Name; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t s false 43] + +-- 835 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (i int NOT NULL); + INSERT INTO t VALUES(42); +COMMIT; +SELECT * FROM __Column2 ORDER BY Name; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t i true ] + +-- 836 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int, c int); + INSERT INTO t VALUES(NULL, NULL, NULL); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[ ] + +-- 837 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int NOT NULL, c int); + INSERT INTO t VALUES(NULL, 42, NULL); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[ 42 ] + +-- 838 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int NOT NULL, c int); + INSERT INTO t VALUES(NULL, NULL, NULL); +COMMIT; +SELECT * FROM t; +||NOT NULL + +-- 839 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int DEFAULT 42, c int); + INSERT INTO t VALUES(NULL, NULL, NULL); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[ 42 ] + +-- 840 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > 42, c int); + INSERT INTO t VALUES(NULL, NULL, NULL); +COMMIT; +SELECT * FROM t; +||constraint violation + +-- 841 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > 42, c int); + INSERT INTO t VALUES(NULL, 42, NULL); +COMMIT; +SELECT * FROM t; +||constraint violation + +-- 842 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > 42, c int); + INSERT INTO t VALUES(NULL, 43, NULL); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[ 43 ] + +-- 843 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > 42 DEFAULT 42, c int); + INSERT INTO t VALUES(NULL, NULL, NULL); +COMMIT; +SELECT * FROM t; +||constraint violation + +-- 844 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > 42 DEFAULT 43, c int); + INSERT INTO t VALUES(NULL, NULL, NULL); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[ 43 ] + +-- 845 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > 42 DEFAULT 43, c int); + INSERT INTO t VALUES(NULL, 42, NULL); +COMMIT; +SELECT * FROM t; +||constraint violation + +-- 846 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > 42 DEFAULT 430, c int); + INSERT INTO t VALUES(NULL, 43, NULL); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[ 43 ] + +-- 847 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > a && b < c, c int); + INSERT INTO t VALUES(1, 2, 3); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[1 2 3] + +-- 848 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > a && b < c, c int); + INSERT INTO t VALUES(1, 1, 3); +COMMIT; +SELECT * FROM t; +||constraint violation + +-- 849 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > a && b < c, c int); + INSERT INTO t VALUES(1, 3, 3); +COMMIT; +SELECT * FROM t; +||constraint violation + +-- 850 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > a && b < c DEFAULT (a+c)/2, c int); + INSERT INTO t VALUES(1, NULL, 3); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[1 2 3] + +-- 851 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > a && b < c DEFAULT (a+c)/2, c int); + INSERT INTO t VALUES(1, 1, 3); +COMMIT; +SELECT * FROM t; +||constraint violation + +-- 852 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int b > a && b < c DEFAULT (a+c)/2, c int); + INSERT INTO t VALUES(1, 3, 3); +COMMIT; +SELECT * FROM t; +||constraint violation + +-- 853 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE department ( + DepartmentName string DepartmentName IN ("HQ", "R/D", "Lab", "HR") + DEFAULT "HQ", + ); + INSERT INTO department VALUES ("foo"); +COMMIT; +SELECT * FROM department; +||constraint violation + +-- 854 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE department ( + DepartmentName string DepartmentName IN ("HQ", "R/D", "Lab", "HR") + DEFAULT "HQ", + ); + INSERT INTO department VALUES ("HQ"); +COMMIT; +SELECT * FROM department; +|"DepartmentName" +[HQ] + +-- 855 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE department ( + DepartmentName string DepartmentName IN ("HQ", "R/D", "Lab", "HR") + DEFAULT "HQ", + ); + INSERT INTO department VALUES (NULL); +COMMIT; +SELECT * FROM department; +|"DepartmentName" +[HQ] + +-- 856 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE department ( + DepartmentName string DepartmentName IN ("HQ", "R/D", "Lab", "HR") + DEFAULT "HQ", + ); + INSERT INTO department VALUES ("R&D"); +COMMIT; +SELECT * FROM department; +||constraint violation + +-- 857 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE department ( + DepartmentName string DepartmentName IN ("HQ", "R/D", "Lab", "HR") + DEFAULT "HQ", + ); + INSERT INTO department VALUES ("R/D"); +COMMIT; +SELECT * FROM department; +|"DepartmentName" +[R/D] + +-- 858 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t ( + TimeStamp time TimeStamp <= now() && since(TimeStamp) < duration("10s") DEFAULT now(), + ); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT TimeStamp IS NOT NULL FROM t; +|"" +[true] + +-- 859 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t ( + TimeStamp time TimeStamp <= now() && since(TimeStamp) < duration("10s") DEFAULT now(), + ); + INSERT INTO t VALUES(now()-duration("11s")); +COMMIT; +SELECT TimeStamp IS NOT NULL FROM t; +||constraint violation + +-- 860 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t ( + TimeStamp time TimeStamp <= now() && since(TimeStamp) < duration("10s") DEFAULT now(), + ); + INSERT INTO t VALUES(now()+duration("1s")); +COMMIT; +SELECT TimeStamp IS NOT NULL FROM t; +||constraint violation + +-- 861 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t ( + TimeStamp time TimeStamp <= now() && since(TimeStamp) < duration("10s") DEFAULT now(), + ); + INSERT INTO t VALUES(now()-duration("1s")); +COMMIT; +SELECT TimeStamp IS NOT NULL FROM t; +|"" +[true] + +-- 862 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t ( + TimeStamp time TimeStamp <= now() && since(TimeStamp) < duration("10s") DEFAULT now(), + ); + INSERT INTO t VALUES(now()); +COMMIT; +SELECT TimeStamp IS NOT NULL FROM t; +|"" +[true] + +-- 863 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t ( + Event string Event != "" && Event LIKE "[0-9]+:[ \t]+.*", + ); + INSERT INTO t VALUES("123 foo"); +COMMIT; +SELECT Event FROM t; +||constraint violation + +-- 864 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t ( + Event string Event != "" && Event LIKE "[0-9]+:[ \t]+.*", + ); + INSERT INTO t VALUES("123: foo"); +COMMIT; +SELECT Event FROM t; +|"Event" +[123: foo] + +-- 865 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int, c int); + CREATE TABLE s (i int); + INSERT INTO s VALUES (1), (2), (NULL), (3), (4); + INSERT INTO t(b) SELECT * FROM s; +COMMIT; +SELECT b FROM t ORDER BY b DESC; +|"b" +[4] +[3] +[2] +[1] +[] + +-- 866 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int NOT NULL, c int); + CREATE TABLE s (i int); + INSERT INTO s VALUES (1), (2), (NULL), (3), (4); + INSERT INTO t(b) SELECT * FROM s WHERE i IS NOT NULL; +COMMIT; +SELECT b FROM t ORDER BY b DESC; +|"b" +[4] +[3] +[2] +[1] + +-- 867 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int NOT NULL, c int); + CREATE TABLE s (i int); + INSERT INTO s VALUES (1), (2), (NULL), (3), (4); + INSERT INTO t(b) SELECT * FROM s; +COMMIT; +SELECT i FROM t ORDER BY b DESC; +||NOT NULL + +-- 868 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int, c int); + INSERT INTO t(b) VALUES (10), (20), (30); + UPDATE t b = NULL WHERE b == 20; +COMMIT; +SELECT b FROM t ORDER BY b DESC; +|"b" +[30] +[10] +[] + +-- 869 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int NOT NULL, c int); + INSERT INTO t(b) VALUES (10), (20), (30); + UPDATE t b = NULL WHERE b == 20; +COMMIT; +SELECT b FROM t ORDER BY b DESC; +||NOT NULL + +-- 870 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t (a int, b int NOT NULL DEFAULT 42, c int); + INSERT INTO t(b) VALUES (10), (20), (30); + UPDATE t b = NULL WHERE b == 20; +COMMIT; +SELECT b FROM t ORDER BY b DESC; +|"b" +[42] +[30] +[10] + +-- S 871 // https://github.com/cznic/ql/issues/91 +SELECT * +FROM employee +LEFT OUTER JOIN department +ON employee.DepartmentID == department.DepartmentID +ORDER BY employee.LastName; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] +[Williams ] + +-- S 872 // https://github.com/cznic/ql/issues/91 +SELECT * +FROM employee +LEFT JOIN department +ON employee.DepartmentID == department.DepartmentID +ORDER BY employee.LastName; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] +[Williams ] + +-- S 873 // https://github.com/cznic/ql/issues/91 +SELECT * +FROM employee +RIGHT OUTER JOIN department +ON employee.DepartmentID == department.DepartmentID +ORDER BY employee.LastName; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[ 35 Marketing] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] + +-- S 874 // https://github.com/cznic/ql/issues/91 +SELECT * +FROM employee +RIGHT JOIN department +ON employee.DepartmentID == department.DepartmentID +ORDER BY employee.LastName; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[ 35 Marketing] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] + +-- S 875 // https://github.com/cznic/ql/issues/91 +SELECT * +FROM employee +FULL OUTER JOIN department +ON employee.DepartmentID == none; +||unknown + +-- S 876 // https://github.com/cznic/ql/issues/91 +SELECT * +FROM employee +FULL JOIN department +ON employee.DepartmentID == none; +||unknown + +-- 877 // https://dev.mysql.com/worklog/task/?id=1604 +BEGIN TRANSACTION; + CREATE TABLE t1 (s1 int); + CREATE TABLE t2 (s1 int); + INSERT INTO t1 VALUES (1); + INSERT INTO t1 VALUES (1); +COMMIT; +SELECT * FROM t1 LEFT JOIN t2 ON t1.s1 == t2.s1; +|"t1.s1", "t2.s1" +[1 ] +[1 ] + +-- 878 // https://github.com/cznic/ql/issues/91 +BEGIN TRANSACTION; + CREATE TABLE a (i int, s string); + INSERT INTO a VALUES (1, "a"), (3, "a"), (NULL, "an1"), (NULL, "an2"); + CREATE TABLE b (i int, s string); + INSERT INTO b VALUES (2, "b"), (3, "b"), (NULL, "bn1"), (NULL, "bn2"); +COMMIT; +SELECT * FROM a LEFT JOIN b ON a.i == b.i +ORDER BY a.s, a.i, b.s, b.i; +|"a.i", "a.s", "b.i", "b.s" +[1 a ] +[3 a 3 b] +[ an1 ] +[ an2 ] + +-- 879 // https://github.com/cznic/ql/issues/91 +BEGIN TRANSACTION; + CREATE TABLE a (i int, s string); + INSERT INTO a VALUES (1, "a"), (3, "a"), (NULL, "an1"), (NULL, "an2"); + CREATE TABLE b (i int, s string); + INSERT INTO b VALUES (2, "b"), (3, "b"), (NULL, "bn1"), (NULL, "bn2"); +COMMIT; +SELECT * FROM a RIGHT JOIN b ON a.i == b.i +ORDER BY a.s, a.i, b.s, b.i; +|"a.i", "a.s", "b.i", "b.s" +[ 2 b] +[ bn1] +[ bn2] +[3 a 3 b] + +-- 880 // https://github.com/cznic/ql/issues/91 +BEGIN TRANSACTION; + CREATE TABLE a (i int, s string); + INSERT INTO a VALUES (1, "a"), (3, "a"), (NULL, "an1"), (NULL, "an2"); + CREATE TABLE b (i int, s string); + INSERT INTO b VALUES (2, "b"), (3, "b"), (NULL, "bn1"), (NULL, "bn2"); +COMMIT; +SELECT * FROM b LEFT JOIN a ON a.i == b.i +ORDER BY a.s, a.i, b.s, b.i; +|"b.i", "b.s", "a.i", "a.s" +[2 b ] +[ bn1 ] +[ bn2 ] +[3 b 3 a] + +-- 881 // https://github.com/cznic/ql/issues/91 +BEGIN TRANSACTION; + CREATE TABLE a (i int, s string); + INSERT INTO a VALUES (1, "a"), (3, "a"), (NULL, "an1"), (NULL, "an2"); + CREATE TABLE b (i int, s string); + INSERT INTO b VALUES (2, "b"), (3, "b"), (NULL, "bn1"), (NULL, "bn2"); +COMMIT; +SELECT * FROM b RIGHT JOIN a ON a.i == b.i +ORDER BY a.s, a.i, b.s, b.i; +|"b.i", "b.s", "a.i", "a.s" +[ 1 a] +[3 b 3 a] +[ an1] +[ an2] + +-- 882 // https://github.com/cznic/ql/issues/91 +BEGIN TRANSACTION; + CREATE TABLE a (i int, s string); + INSERT INTO a VALUES (1, "a"), (3, "a"); + CREATE TABLE b (i int, s string); + INSERT INTO b VALUES (2, "b"), (3, "b"); +COMMIT; +SELECT * FROM a FULL JOIN b ON a.i == b.i +ORDER BY a.s, a.i, b.s, b.i; +|"a.i", "a.s", "b.i", "b.s" +[ 2 b] +[1 a ] +[3 a 3 b] + +-- 883 // https://github.com/cznic/ql/issues/91 +BEGIN TRANSACTION; + CREATE TABLE a (i int, s string); + INSERT INTO a VALUES (1, "a"), (3, "a"); + CREATE TABLE b (i int, s string); + INSERT INTO b VALUES (2, "b"), (3, "b"); +COMMIT; +SELECT * FROM a FULL OUTER JOIN b ON a.i == b.i +ORDER BY a.s, a.i, b.s, b.i; +|"a.i", "a.s", "b.i", "b.s" +[ 2 b] +[1 a ] +[3 a 3 b] + +-- S 884 // https://github.com/cznic/ql/issues/91 +SELECT * +FROM employee +FULL JOIN department +ON employee.DepartmentID == department.DepartmentID +ORDER BY employee.LastName; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[ 35 Marketing] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] +[Williams ] + +-- S 885 // https://github.com/cznic/ql/issues/91 +SELECT * +FROM employee +FULL OUTER JOIN department +ON employee.DepartmentID == department.DepartmentID +ORDER BY employee.LastName; +|"employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[ 35 Marketing] +[Heisenberg 33 33 Engineering] +[Jones 33 33 Engineering] +[Rafferty 31 31 Sales] +[Robinson 34 34 Clerical] +[Smith 34 34 Clerical] +[Williams ] + +-- S 886 // https://github.com/cznic/ql/issues/91 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES ("A"), ("B"); +COMMIT; +SELECT * +FROM t, employee +LEFT JOIN department +ON employee.DepartmentID == department.DepartmentID +ORDER BY t.s, employee.LastName; +|"t.s", "employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[A Heisenberg 33 33 Engineering] +[A Jones 33 33 Engineering] +[A Rafferty 31 31 Sales] +[A Robinson 34 34 Clerical] +[A Smith 34 34 Clerical] +[A Williams ] +[B Heisenberg 33 33 Engineering] +[B Jones 33 33 Engineering] +[B Rafferty 31 31 Sales] +[B Robinson 34 34 Clerical] +[B Smith 34 34 Clerical] +[B Williams ] + +-- S 887 // https://github.com/cznic/ql/issues/91 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES ("A"), ("B"); +COMMIT; +SELECT * +FROM t, employee +RIGHT JOIN department +ON employee.DepartmentID == department.DepartmentID +ORDER BY t.s, employee.LastName; +|"t.s", "employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[ 35 Marketing] +[A Heisenberg 33 33 Engineering] +[A Jones 33 33 Engineering] +[A Rafferty 31 31 Sales] +[A Robinson 34 34 Clerical] +[A Smith 34 34 Clerical] +[B Heisenberg 33 33 Engineering] +[B Jones 33 33 Engineering] +[B Rafferty 31 31 Sales] +[B Robinson 34 34 Clerical] +[B Smith 34 34 Clerical] + +-- S 888 // https://github.com/cznic/ql/issues/91 +BEGIN TRANSACTION; + CREATE TABLE t (s string); + INSERT INTO t VALUES ("A"), ("B"); +COMMIT; +SELECT * +FROM t, employee +FULL JOIN department +ON employee.DepartmentID == department.DepartmentID +ORDER BY t.s, employee.LastName; +|"t.s", "employee.LastName", "employee.DepartmentID", "department.DepartmentID", "department.DepartmentName" +[ 35 Marketing] +[A Heisenberg 33 33 Engineering] +[A Jones 33 33 Engineering] +[A Rafferty 31 31 Sales] +[A Robinson 34 34 Clerical] +[A Smith 34 34 Clerical] +[A Williams ] +[B Heisenberg 33 33 Engineering] +[B Jones 33 33 Engineering] +[B Rafferty 31 31 Sales] +[B Robinson 34 34 Clerical] +[B Smith 34 34 Clerical] +[B Williams ] + +-- 889 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t1 (a int, b int, c int); + CREATE TABLE t2 (a int, b int NOT NULL, c int); + CREATE TABLE t3 (a int, b int a < b && b < c, c int); + CREATE TABLE t4 (a int, b int a < b && b < c DEFAULT (a+c)/2, c int); + CREATE TABLE t5 (a int, b int NOT NULL DEFAULT (a+c)/2, c int); + CREATE TABLE t6 (a int, b int DEFAULT (a+c)/2, c int); +COMMIT; +SELECT * FROM __Table WHERE !hasPrefix(Name, "__") ORDER BY Name; +|"Name", "Schema" +[t1 CREATE TABLE t1 (a int64, b int64, c int64);] +[t2 CREATE TABLE t2 (a int64, b int64 NOT NULL, c int64);] +[t3 CREATE TABLE t3 (a int64, b int64 a < b && b < c, c int64);] +[t4 CREATE TABLE t4 (a int64, b int64 a < b && b < c DEFAULT (a + c) / 2, c int64);] +[t5 CREATE TABLE t5 (a int64, b int64 NOT NULL DEFAULT (a + c) / 2, c int64);] +[t6 CREATE TABLE t6 (a int64, b int64 DEFAULT (a + c) / 2, c int64);] + +-- 890 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t1 (a int, b int, c int); + CREATE TABLE t2 (a int, b int NOT NULL, c int); + CREATE TABLE t3 (a int, b int a < b && b < c, c int); + CREATE TABLE t4 (a int, b int a < b && b < c DEFAULT (a+c)/2, c int); + CREATE TABLE t5 (a int, b int NOT NULL DEFAULT (a+c)/2, c int); + CREATE TABLE t6 (a int, b int DEFAULT (a+c)/2, c int); +COMMIT; +SELECT * FROM __Column2 ORDER BY TableName, Name; +|"TableName", "Name", "NotNull", "ConstraintExpr", "DefaultExpr" +[t2 b true ] +[t3 b false a < b && b < c ] +[t4 b false a < b && b < c (a + c) / 2] +[t5 b true (a + c) / 2] +[t6 b false (a + c) / 2] + +-- 891 // https://github.com/cznic/ql/issues/85 +BEGIN TRANSACTION; + CREATE TABLE t1 (a int, b int, c int); + CREATE TABLE t2 (a int, b int NOT NULL, c int); + CREATE TABLE t3 (a int, b int a < b && b < c, c int); + CREATE TABLE t4 (a int, b int a < b && b < c DEFAULT (a+c)/2, c int); + CREATE TABLE t5 (a int, b int NOT NULL DEFAULT (a+c)/2, c int); + CREATE TABLE t6 (a int, b int DEFAULT (a+c)/2, c int); +COMMIT; +SELECT + __Column.TableName, __Column.Ordinal, __Column.Name, __Column.Type, + __Column2.NotNull, __Column2.ConstraintExpr, __Column2.DefaultExpr, +FROM __Column +LEFT JOIN __Column2 +ON __Column.TableName == __Column2.TableName && __Column.Name == __Column2.Name +WHERE !hasPrefix(__Column.TableName, "__") +ORDER BY __Column.TableName, __Column.Ordinal; +|"__Column.TableName", "__Column.Ordinal", "__Column.Name", "__Column.Type", "__Column2.NotNull", "__Column2.ConstraintExpr", "__Column2.DefaultExpr" +[t1 1 a int64 ] +[t1 2 b int64 ] +[t1 3 c int64 ] +[t2 1 a int64 ] +[t2 2 b int64 true ] +[t2 3 c int64 ] +[t3 1 a int64 ] +[t3 2 b int64 false a < b && b < c ] +[t3 3 c int64 ] +[t4 1 a int64 ] +[t4 2 b int64 false a < b && b < c (a + c) / 2] +[t4 3 c int64 ] +[t5 1 a int64 ] +[t5 2 b int64 true (a + c) / 2] +[t5 3 c int64 ] +[t6 1 a int64 ] +[t6 2 b int64 false (a + c) / 2] +[t6 3 c int64 ] + +-- 892 +BEGIN TRANSACTION; + DROP TABLE __Index2; +COMMIT; +||system table + +-- 893 +BEGIN TRANSACTION; + CREATE TABLE __Index2 (i int); +COMMIT; +||system table + +-- 894 +BEGIN TRANSACTION; + UPDATE __Index2 SET i = 42; +COMMIT; +||system table + +-- 895 +BEGIN TRANSACTION; + CREATE INDEX __Index2X ON __Index2(x); +COMMIT; +||system table + +-- 896 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM __Index2; +||does not exist + +-- 897 +BEGIN TRANSACTION; + DROP TABLE __Index2_Expr; +COMMIT; +||system table + +-- 898 +BEGIN TRANSACTION; + CREATE TABLE __Index2_Expr (i int); +COMMIT; +||system table + +-- 899 +BEGIN TRANSACTION; + UPDATE __Index2_Expr SET i = 42; +COMMIT; +||system table + +-- 900 +BEGIN TRANSACTION; + CREATE INDEX __Index2X ON __Index2_Expr(x); +COMMIT; +||system table + +-- 901 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +SELECT * FROM __Index2_Expr; +||does not exist + +-- 902 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__"); +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false true true] + +-- 903 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" +); +|"Expr" +[i] + +-- 904 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__"); +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false true true] + +-- 905 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" +); +|"Expr" +[id()] + +-- 906 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE unique INDEX x ON t(i); + CREATE INDEX y ON t(id()); +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x true true true] +[t y false true true] + +-- 907 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON t(id()); +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[i] +[id()] + +-- 908 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON t(id()); + DROP INDEX x; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t y false true true] + +-- 909 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON t(id()); + DROP INDEX y; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false true true] + +-- 910 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON t(id()); + DROP INDEX x; + DROP INDEX y; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" + +-- 911 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON t(id()); + DROP INDEX x; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[id()] + +-- 912 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON t(id()); + DROP INDEX y; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[i] + +-- 913 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON t(id()); + DROP INDEX x; + DROP INDEX y; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" + +-- 914 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false true true] +[u y false true true] + +-- 915 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + DROP TABLE t; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[u y false true true] + +-- 916 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + DROP TABLE u; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false true true] + +-- 917 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + DROP TABLE t; + DROP TABLE u; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" + +-- 918 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + DROP TABLE u; + DROP TABLE t; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" + +-- 919 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[i] +[j] + +-- 920 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + DROP TABLE t; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[j] + +-- 921 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + DROP TABLE u; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[i] + +-- 922 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + DROP TABLE t; + DROP TABLE u; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" + +-- 923 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE TABLE u (j int); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + DROP TABLE u; + DROP TABLE t; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" + +-- 924 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false true true] +[u y false true true] + +-- 925 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE t DROP COLUMN i; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[u y false true true] + +-- 926 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE t DROP COLUMN a; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false true true] +[u y false true true] + +-- 927 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE u DROP COLUMN j; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false true true] + +-- 928 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE u DROP COLUMN b; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false true true] +[u y false true true] + +-- 929 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE t DROP COLUMN i; + ALTER TABLE u DROP COLUMN j; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" + +-- 930 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE u DROP COLUMN j; + ALTER TABLE t DROP COLUMN i; +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: memory DB +FROM __Index2 +WHERE !hasPrefix(TableName, "__") +ORDER BY IndexName; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" + +-- 931 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[i] +[j] + +-- 932 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE t DROP COLUMN i; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[j] + +-- 933 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE t DROP COLUMN a; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[i] +[j] + +-- 934 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE u DROP COLUMN j; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[i] + +-- 935 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE u DROP COLUMN b; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" +[i] +[j] + +-- 936 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE t DROP COLUMN i; + ALTER TABLE u DROP COLUMN j; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" + +-- 937 +BEGIN TRANSACTION; + CREATE TABLE t (i int, a string); + CREATE TABLE u (j int, b string); + CREATE INDEX x ON t(i); + CREATE INDEX y ON u(j); + ALTER TABLE u DROP COLUMN j; + ALTER TABLE t DROP COLUMN i; +COMMIT; +SELECT Expr +FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" OR IndexName == "y" +) +ORDER BY Expr; +|"Expr" + +-- 938 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a + c, c - b); +COMMIT; +SELECT TableName, IndexName, IsUnique, IsSimple, Root > 0 OR Root == -1 // -1: mem DB +FROM __Index2 +WHERE TableName == "t"; +|"TableName", "IndexName", "IsUnique", "IsSimple", "" +[t x false false true] + +-- 939 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a + c, c - b); +COMMIT; +SELECT Expr FROM __Index2_Expr +WHERE Index2_ID IN ( + SELECT id() + FROM __Index2 + WHERE IndexName == "x" +) +ORDER BY Expr; +|"Expr" +[a + c] +[c - b] + +-- 940 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + INSERT INTO t VALUES(1, 2, 3); + CREATE INDEX x ON t(a + c, c - b); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[1 2 3] + +-- 941 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + INSERT INTO t VALUES(1, 2, 3); + CREATE INDEX x ON t(a + c, c - b); +COMMIT; +SELECT * FROM x; +|"x" +[4 1] + +-- 942 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a + c, c - b); + INSERT INTO t VALUES(1, 2, 3); +COMMIT; +SELECT * FROM t; +|"a", "b", "c" +[1 2 3] + +-- 943 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a + c, c - b); + INSERT INTO t VALUES(1, 2, 3); +COMMIT; +SELECT * FROM x; +|"x" +[4 1] + +-- 944 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a + c, c - b); + DROP INDEX x; +COMMIT; +SELECT * FROM x; +||x does not exist + +-- 945 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a + c, c - b); + DROP TABLE t; +COMMIT; +SELECT * FROM x; +||x does not exist + +-- 946 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a + c, c - b); + ALTER TABLE t DROP COLUMN a; +COMMIT; +SELECT * FROM x; +||x does not exist + +-- 947 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a + c, c - b); + ALTER TABLE t DROP COLUMN b; +COMMIT; +SELECT * FROM x; +||x does not exist + +-- 948 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a + c, c - b); + ALTER TABLE t DROP COLUMN c; +COMMIT; +SELECT * FROM x; +||x does not exist + +-- 949 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a, c); + ALTER TABLE t DROP COLUMN a; +COMMIT; +SELECT * FROM x; +||x does not exist + +-- 950 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a, c); + ALTER TABLE t DROP COLUMN b; +COMMIT; +SELECT * FROM x; +|"x" + +-- 951 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a, c); + ALTER TABLE t DROP COLUMN a; +COMMIT; +SELECT * FROM x; +||x does not exist + +-- 952 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a, c); + INSERT INTO t VALUES(1, 2, 3); + ALTER TABLE t DROP COLUMN b; +COMMIT; +SELECT * FROM x; +|"x" +[1 3] + +-- 953 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(a, c); + INSERT INTO t VALUES(10, 20, 30); + ALTER TABLE t DROP COLUMN b; + INSERT INTO t VALUES(1, 3); + ALTER TABLE t ADD b string; + INSERT INTO t VALUES(5, 15, "foo"); +COMMIT; +SELECT * FROM x; +|"x" +[1 3] +[5 15] +[10 30] + +-- 954 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(b, c); + INSERT INTO t VALUES (100, 200, 300), (1, 2, 3), (10, 20, 30); +COMMIT; +SELECT * FROM x; +|"x" +[2 3] +[20 30] +[200 300] + +-- 955 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(b); + INSERT INTO t VALUES (100, 200, 300), (1, 2, 3), (10, 20, 30); + INSERT INTO t VALUES (NULL, 200, 300), (1, NULL, 3), (10, NULL, 30), (NULL, NULL, NULL); +COMMIT; +SELECT * FROM x; +|"x" +[] +[] +[] +[2] +[20] +[200] +[200] + +-- 956 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(b, c); + INSERT INTO t VALUES + (100, 200, 300), (1, 2, 3), (10, 20, 30), + (NULL, 200, 300), (1, NULL, 3), (10, NULL, 30), + (NULL, NULL, NULL); +COMMIT; +SELECT * FROM x; +|"x" +[ ] +[ 3] +[ 30] +[2 3] +[20 30] +[200 300] +[200 300] + +-- 957 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(b, c); + INSERT INTO t VALUES + (100, 200, 300), (1, 2, 3), (10, 20, 30), + (NULL, 200, 300), (1, NULL, 3), (10, NULL, 30), + (NULL, NULL, NULL), (NULL, NULL, NULL); +COMMIT; +SELECT * FROM x; +|"x" +[ ] +[ ] +[ 3] +[ 30] +[2 3] +[20 30] +[200 300] +[200 300] + +-- 958 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE UNIQUE INDEX x ON t(b, c); + INSERT INTO t VALUES + (100, 200, 300), (1, 2, 3), (10, 20, 30), + (NULL, 200, 300), (1, NULL, 3), (10, NULL, 30), + (NULL, NULL, NULL), (NULL, NULL, NULL); +COMMIT; +SELECT * FROM x; +||duplicate .* [200 300] + +-- 959 +BEGIN TRANSACTION; + CREATE TABLE t(a int, b int, c int); + CREATE INDEX x ON t(b, c); + INSERT INTO t VALUES + (100, 200, 300), (1, 2, 3), (10, 20, 30), + (NULL, 200, 301), (1, NULL, 3), (10, NULL, 30), + (NULL, NULL, NULL), (NULL, NULL, NULL); +COMMIT; +SELECT * FROM x; +|"x" +[ ] +[ ] +[ 3] +[ 30] +[2 3] +[20 30] +[200 300] +[200 301] + +-- 960 +BEGIN TRANSACTION; + CREATE INDEX x ON t (qty()+1); +COMMIT; +||undefined.* qty + +-- 961 +BEGIN TRANSACTION; + CREATE INDEX x ON t (qty+1); +COMMIT; +||table.*not exist + +-- 962 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (qty+1); +COMMIT; +||column.*not exist + +-- 963 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (id()+1); +COMMIT; +SELECT * FROM t; +|"c" + +-- 964 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (id()+1); + CREATE INDEX y ON t (id()+1); +COMMIT; +SELECT * FROM t; +|"c" + +-- 965 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (id()+1); + CREATE INDEX x ON t (c+1); +COMMIT; +SELECT * FROM t; +||already + +-- 966 +BEGIN TRANSACTION; + CREATE TABLE t (c int); + CREATE INDEX x ON t (c+1); + INSERT INTO t VALUES(42); +COMMIT; +SELECT * FROM x; +|"x" +[43] + +-- 967 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i+1000); + INSERT INTO t VALUES(42); + INSERT INTO t VALUES(24); + CREATE INDEX y ON t (i+2000); + INSERT INTO t VALUES(1); + INSERT INTO t VALUES(999); + UPDATE t i = 240 WHERE i == 24; + DELETE FROM t WHERE i == 240; +COMMIT; +SELECT * FROM x; +|"x" +[1001] +[1042] +[1999] + +-- 968 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i+1000); + INSERT INTO t VALUES(42); + INSERT INTO t VALUES(24); + CREATE INDEX y ON t (i+2000); + INSERT INTO t VALUES(1); + INSERT INTO t VALUES(999); + UPDATE t i = 240 WHERE i == 24; + DELETE FROM t WHERE i == 240; +COMMIT; +SELECT * FROM y; +|"y" +[2001] +[2042] +[2999] + +-- 969 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX i ON t (i+1); +COMMIT; +||collision .*: i + + +-- 970 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i+1); +COMMIT; +BEGIN TRANSACTION; + INSERT INTO t VALUES(1000); + BEGIN TRANSACTION; + INSERT INTO t VALUES(2000); + ROLLBACK; + INSERT INTO t VALUES(3000); +COMMIT; +SELECT * FROM x; +|"x" +[1001] +[3001] + +-- 971 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i+1); + INSERT INTO t VALUES (42); + TRUNCATE TABLE t; +COMMIT; +SELECT * FROM x; +|"x" + +-- 972 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i+1); + INSERT INTO t VALUES (42); + DELETE FROM t; +COMMIT; +SELECT * FROM x; +|"x" + +-- 973 +BEGIN TRANSACTION; + CREATE TABLE t (i int, s string); + CREATE INDEX x ON t (i+1); + INSERT INTO t VALUES (42, "foo"); + ALTER TABLE t DROP COLUMN i; + INSERT INTO t VALUES ("bar"); +COMMIT; +SELECT * FROM x; +||x does not exist + +-- 974 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t (i+1); + INSERT INTO t VALUES(NULL); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(30); + INSERT INTO t VALUES(20); + INSERT INTO t VALUES(40); + INSERT INTO t VALUES(10); + INSERT INTO t VALUES(50); + INSERT INTO t VALUES(NULL); +COMMIT; +SELECT * FROM x; +|"x" +[] +[] +[11] +[11] +[21] +[21] +[31] +[31] +[41] +[41] +[51] +[51] + +-- 975 +BEGIN TRANSACTION; + CREATE TABLE t (i blob); + CREATE INDEX x ON t (blob(string(i))); +COMMIT; +SELECT * FROM x; +|"x" + +-- 976 +BEGIN TRANSACTION; + CREATE TABLE t (i blob); + CREATE INDEX x ON t (blob(string(i))); + INSERT INTO t VALUES (blob("foo")); +COMMIT; +SELECT * FROM x; +||blob-like + +-- 977 +BEGIN TRANSACTION; + CREATE TABLE t (i bigint); + CREATE INDEX x ON t (i+1); + INSERT INTO t VALUES (42); +COMMIT; +SELECT * FROM x; +||blob-like + +-- 978 +BEGIN TRANSACTION; + CREATE TABLE t (i bigrat); + CREATE INDEX x ON t (i+1); + INSERT INTO t VALUES (42); +COMMIT; +SELECT * FROM x; +||blob-like + +-- 979 +BEGIN TRANSACTION; + CREATE TABLE t (i time); + CREATE INDEX x ON t (timeIn(i, "local")); + INSERT INTO t VALUES (now()); +COMMIT; +SELECT * FROM x; +||blob-like + +-- 980 +BEGIN TRANSACTION; + CREATE TABLE t (i duration); + CREATE INDEX x ON t (since(now())); + INSERT INTO t VALUES (duration("3s")); +COMMIT; +SELECT * FROM x; +||blob-like + +-- 981 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +EXPLAIN SELECT * FROM t; +|"" +[┌Iterate all rows of table "t"] +[└Output field names ["i"]] + +-- 982 +BEGIN TRANSACTION; + CREATE TABLE t (i int); +COMMIT; +EXPLAIN EXPLAIN SELECT * FROM t; +|"" +[┌Iterate all rows of table "t"] +[└Output field names ["i"]] + +-- 983 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (42), (-1), (278); +COMMIT; +SELECT * FROM t WHERE i != 42; +|"i" +[278] +[-1] +[0] +[314] + +-- 984 // order -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (314), (0), (NULL), (42), (-1), (278); +COMMIT; +SELECT * FROM t WHERE i != 42; +|"i" +[-1] +[0] +[278] +[314] + +-- 985 // order -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE i != 42; +|"i" +[-1] +[0] +[278] +[314] + +-- 986 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() > 0; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 987 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() > 0; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 988 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int); + INSERT INTO t VALUES (314, 100), (0, 200), (NULL, 300), (-1, 400), (278, 500); +COMMIT; +SELECT * FROM t WHERE i IS NULL; +|"i", "j" +[ 300] + +-- 989 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (314, 100), (0, 200), (NULL, 300), (-1, 400), (278, 500); +COMMIT; +SELECT * FROM t WHERE i IS NULL; +|"i", "j" +[ 300] + +-- 990 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE i IS NOT NULL; +|"i" +[278] +[-1] +[0] +[314] + +-- 991 // order -> index is used +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); + CREATE INDEX x ON t(i); +COMMIT; +SELECT * FROM t WHERE i IS NOT NULL; +|"i" +[-1] +[0] +[278] +[314] + +-- 992 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() IS NULL; +|"i" + +-- 993 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() IS NOT NULL; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 994 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() IS NULL; +|"i" + +-- 995 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() IS NOT NULL; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 996 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() == 0; +|"i" + +-- 997 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() == 0; +|"i" + +-- 998 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() < 1; +|"i" + +-- 999 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() < 1; +|"i" + +-- 1000 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() <= 0; +|"i" + +-- 1001 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() <= 0; +|"i" + +-- 1002 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() > 0; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 1003 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() > 0; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 1004 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() >= 1; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 1005 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() >= 1; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 1006 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() != 0; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 1007 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(id()); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE id() != 0; +|"i" +[278] +[-1] +[] +[0] +[314] + +-- 1008 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (314), (0), (NULL), (-1), (278); +COMMIT; +SELECT * FROM t WHERE -1 < i && 314 > i OR i > 1000 && i < 2000; //MAYBE use ORed intervals +|"i" +[278] +[0] + +-- 1009 +BEGIN TRANSACTION; + CREATE TABLE t (i int, b bool); + CREATE INDEX x ON t (b); + INSERT INTO t VALUES(24, false); + INSERT INTO t VALUES(333, NULL); + INSERT INTO t VALUES(42, true); + INSERT INTO t VALUES(240, false); + INSERT INTO t VALUES(420, true); +COMMIT; +SELECT i FROM t WHERE !b ORDER BY i; +|"i" +[24] +[240] + +-- 1010 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i == -2; +|"i" + +-- 1011 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i == -1; +|"i" + +-- 1012 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i == 0; +|"i" +[0] + +-- 1013 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i == 1; +|"i" + +-- 1014 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i == 2; +|"i" + +-- 1015 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i >= -2; +|"i" +[0] + +-- 1016 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i >= -1; +|"i" +[0] + +-- 1017 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i >= 0; +|"i" +[0] + +-- 1018 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i >= 1; +|"i" + +-- 1019 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i >= 2; +|"i" + +-- 1020 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i > -2; +|"i" +[0] + +-- 1021 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i > -1; +|"i" +[0] + +-- 1022 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i > 0; +|"i" + +-- 1023 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i > 1; +|"i" + +-- 1024 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i > 2; +|"i" + +-- 1025 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i <= -2; +|"i" + +-- 1026 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i <= -1; +|"i" + +-- 1027 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i <= 0; +|"i" +[0] + +-- 1028 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i <= 1; +|"i" +[0] + +-- 1029 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i <= 2; +|"i" +[0] + +-- 1030 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i < -2; +|"i" + +-- 1031 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i < -1; +|"i" + +-- 1032 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i < 0; +|"i" + +-- 1033 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i < 1; +|"i" +[0] + +-- 1034 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i < 2; +|"i" +[0] + +-- 1035 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i != -2; +|"i" +[0] + +-- 1036 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i != -1; +|"i" +[0] + +-- 1037 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i != 0; +|"i" + +-- 1038 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i != 1; +|"i" +[0] + +-- 1039 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i == 0 && i != 2; +|"i" +[0] + +-- 1040 +BEGIN TRANSACTION; + CREATE TABLE t (i int, b bool); + CREATE INDEX x ON t(b); + INSERT INTO t VALUES (1, false), (NULL, NULL), (-2, false), (0, true), (2, false), (-1, true); +COMMIT; +SELECT * FROM t WHERE !b && b ORDER BY i; +|"i", "b" + +-- 1041 +BEGIN TRANSACTION; + CREATE TABLE t (i int, b bool); + CREATE INDEX x ON t(b); + INSERT INTO t VALUES (1, false), (NULL, NULL), (-2, false), (0, true), (2, false), (-1, true); +COMMIT; +SELECT * FROM t WHERE !b && !b ORDER BY i; +|"i", "b" +[-2 false] +[1 false] +[2 false] + +-- 1042 +BEGIN TRANSACTION; + CREATE TABLE t (i int, b bool); + CREATE INDEX x ON t(b); + INSERT INTO t VALUES (1, false), (NULL, NULL), (-2, false), (0, true), (2, false), (-1, true); +COMMIT; +SELECT * FROM t WHERE b && !b ORDER BY i; +|"i", "b" + +-- 1043 +BEGIN TRANSACTION; + CREATE TABLE t (i int, b bool); + CREATE INDEX x ON t(b); + INSERT INTO t VALUES (1, false), (NULL, NULL), (-2, false), (0, true), (2, false), (-1, true); +COMMIT; +SELECT * FROM t WHERE b && b ORDER BY i; +|"i", "b" +[-1 true] +[0 true] + +-- 1044 +SELECT * FROM nothing; // align 5 +||. + +-- 1045 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i == -2; +|"i" + +-- 1046 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i == -1; +|"i" + +-- 1047 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i == 0; +|"i" +[0] + +-- 1048 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i == 1; +|"i" +[1] + +-- 1049 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i == 2; +|"i" +[2] + +-- 1050 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i >= -2; +|"i" +[0] +[1] +[2] + +-- 1051 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i >= -1; +|"i" +[0] +[1] +[2] + +-- 1052 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i >= 0; +|"i" +[0] +[1] +[2] + +-- 1053 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i >= 1; +|"i" +[1] +[2] + +-- 1054 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i >= 2; +|"i" +[2] + +-- 1055 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i > -2; +|"i" +[0] +[1] +[2] + +-- 1056 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i > -1; +|"i" +[0] +[1] +[2] + +-- 1057 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i > 0; +|"i" +[1] +[2] + +-- 1058 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i > 1; +|"i" +[2] + +-- 1059 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i > 2; +|"i" + +-- 1060 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i <= -2; +|"i" + +-- 1061 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i <= -1; +|"i" + +-- 1062 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i <= 0; +|"i" +[0] + +-- 1063 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i <= 1; +|"i" +[0] +[1] + +-- 1064 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i <= 2; +|"i" +[0] +[1] +[2] + +-- 1065 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i < -2; +|"i" + +-- 1066 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i < -1; +|"i" + +-- 1067 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i < 0; +|"i" + +-- 1068 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i < 1; +|"i" +[0] + +-- 1069 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i < 2; +|"i" +[0] +[1] + +-- 1070 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i != -2; +|"i" +[0] +[1] +[2] + +-- 1071 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i != -1; +|"i" +[0] +[1] +[2] + +-- 1072 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i != 0; +|"i" +[1] +[2] + +-- 1073 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i != 1; +|"i" +[0] +[2] + +-- 1074 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= 0 && i != 2; +|"i" +[0] +[1] + +-- 1075 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i == -2; +|"i" + +-- 1076 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i == -1; +|"i" + +-- 1077 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i == 0; +|"i" + +-- 1078 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i == 1; +|"i" +[1] + +-- 1079 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i == 2; +|"i" +[2] + +-- 1080 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i >= -2; +|"i" +[1] +[2] + +-- 1081 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i >= -1; +|"i" +[1] +[2] + +-- 1082 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i >= 0; +|"i" +[1] +[2] + +-- 1083 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i >= 1; +|"i" +[1] +[2] + +-- 1084 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i >= 2; +|"i" +[2] + +-- 1085 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i > -2; +|"i" +[1] +[2] + +-- 1086 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i > -1; +|"i" +[1] +[2] + +-- 1087 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i > 0; +|"i" +[1] +[2] + +-- 1088 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i > 1; +|"i" +[2] + +-- 1089 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i > 2; +|"i" + +-- 1090 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i <= -2; +|"i" + +-- 1091 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i <= -1; +|"i" + +-- 1092 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i <= 0; +|"i" + +-- 1093 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i <= 1; +|"i" +[1] + +-- 1094 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i <= 2; +|"i" +[1] +[2] + +-- 1095 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i < -2; +|"i" + +-- 1096 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i < -1; +|"i" + +-- 1097 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i < 0; +|"i" + +-- 1098 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i < 1; +|"i" + +-- 1099 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i < 2; +|"i" +[1] + +-- 1100 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i != -2; +|"i" +[1] +[2] + +-- 1101 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i != -1; +|"i" +[1] +[2] + +-- 1102 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i != 0; +|"i" +[1] +[2] + +-- 1103 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i != 1; +|"i" +[2] + +-- 1104 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > 0 && i != 2; +|"i" +[1] + +-- 1105 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i == -2; +|"i" +[-2] + +-- 1106 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i == -1; +|"i" +[-1] + +-- 1107 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i == 0; +|"i" +[0] + +-- 1108 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i == 1; +|"i" + +-- 1109 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i == 2; +|"i" + +-- 1110 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i >= -2; +|"i" +[-2] +[-1] +[0] + +-- 1111 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i >= -1; +|"i" +[-1] +[0] + +-- 1112 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i >= 0; +|"i" +[0] + +-- 1113 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i >= 1; +|"i" + +-- 1114 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i >= 2; +|"i" + +-- 1115 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i > -2; +|"i" +[-1] +[0] + +-- 1116 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i > -1; +|"i" +[0] + +-- 1117 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i > 0; +|"i" + +-- 1118 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i > 1; +|"i" + +-- 1119 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i > 2; +|"i" + +-- 1120 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i <= -2; +|"i" +[-2] + +-- 1121 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i <= -1; +|"i" +[-2] +[-1] + +-- 1122 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i <= 0; +|"i" +[-2] +[-1] +[0] + +-- 1123 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i <= 1; +|"i" +[-2] +[-1] +[0] + +-- 1124 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i <= 2; +|"i" +[-2] +[-1] +[0] + +-- 1125 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i < -2; +|"i" + +-- 1126 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i < -1; +|"i" +[-2] + +-- 1127 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i < 0; +|"i" +[-2] +[-1] + +-- 1128 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i < 1; +|"i" +[-2] +[-1] +[0] + +-- 1129 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i < 2; +|"i" +[-2] +[-1] +[0] + +-- 1130 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i != -2; +|"i" +[-1] +[0] + +-- 1131 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i != -1; +|"i" +[-2] +[0] + +-- 1132 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i != 0; +|"i" +[-2] +[-1] + +-- 1133 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i != 1; +|"i" +[-2] +[-1] +[0] + +-- 1134 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i <= 0 && i != 2; +|"i" +[-2] +[-1] +[0] + +-- 1135 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i == -2; +|"i" +[-2] + +-- 1136 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i == -1; +|"i" +[-1] + +-- 1137 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i == 0; +|"i" + +-- 1138 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i == 1; +|"i" + +-- 1139 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i == 2; +|"i" + +-- 1140 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i >= -2; +|"i" +[-2] +[-1] + +-- 1141 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i >= -1; +|"i" +[-1] + +-- 1142 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i >= 0; +|"i" + +-- 1143 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i >= 1; +|"i" + +-- 1144 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i >= 2; +|"i" + +-- 1145 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i > -2; +|"i" +[-1] + +-- 1146 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i > -1; +|"i" + +-- 1147 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i > 0; +|"i" + +-- 1148 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i > 1; +|"i" + +-- 1149 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i > 2; +|"i" + +-- 1150 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i <= -2; +|"i" +[-2] + +-- 1151 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i <= -1; +|"i" +[-2] +[-1] + +-- 1152 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i <= 0; +|"i" +[-2] +[-1] + +-- 1153 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i <= 1; +|"i" +[-2] +[-1] + +-- 1154 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i <= 2; +|"i" +[-2] +[-1] + +-- 1155 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i < -2; +|"i" + +-- 1156 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i < -1; +|"i" +[-2] + +-- 1157 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i < 0; +|"i" +[-2] +[-1] + +-- 1158 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i < 1; +|"i" +[-2] +[-1] + +-- 1159 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i < 2; +|"i" +[-2] +[-1] + +-- 1160 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i != -2; +|"i" +[-1] +-- 1161 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i != -1; +|"i" +[-2] + +-- 1162 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i != 0; +|"i" +[-2] +[-1] + +-- 1163 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i != 1; +|"i" +[-2] +[-1] + +-- 1164 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i < 0 && i != 2; +|"i" +[-2] +[-1] + +-- 1165 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == -2; +|"i" + +-- 1166 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == -1; +|"i" +[-1] + +-- 1167 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == 0; +|"i" +[0] + +-- 1168 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == 1; +|"i" +[1] + +-- 1169 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i == 2; +|"i" + +-- 1170 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= -2; +|"i" +[-1] +[0] +[1] + +-- 1171 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= -1; +|"i" +[-1] +[0] +[1] + +-- 1172 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= 0; +|"i" +[0] +[1] + +-- 1173 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= 1; +|"i" +[1] + +-- 1174 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i >= 2; +|"i" + +-- 1175 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > -2; +|"i" +[-1] +[0] +[1] + +-- 1176 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > -1; +|"i" +[0] +[1] + +-- 1177 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > 0; +|"i" +[1] + +-- 1178 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > 1; +|"i" + +-- 1179 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i > 2; +|"i" + +-- 1180 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= -2; +|"i" + +-- 1181 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= -1; +|"i" +[-1] + +-- 1182 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= 0; +|"i" +[-1] +[0] + +-- 1183 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= 1; +|"i" +[-1] +[0] +[1] + +-- 1184 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i <= 2; +|"i" +[-1] +[0] +[1] + +-- 1185 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < -2; +|"i" + +-- 1186 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < -1; +|"i" + +-- 1187 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < 0; +|"i" +[-1] + +-- 1188 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < 1; +|"i" +[-1] +[0] + +-- 1189 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i < 2; +|"i" +[-1] +[0] +[1] + +-- 1190 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != -2; +|"i" +[-1] +[0] +[1] + +-- 1191 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != -1; +|"i" +[0] +[1] + +-- 1192 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != 0; +|"i" +[-1] +[1] + +-- 1193 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != 1; +|"i" +[-1] +[0] + +-- 1194 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i <= 1 && i != 2; +|"i" +[-1] +[0] +[1] + +-- 1195 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i == -2; +|"i" + +-- 1196 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i == -1; +|"i" + +-- 1197 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i == 0; +|"i" +[0] + +-- 1198 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i == 1; +|"i" +[1] + +-- 1199 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i == 2; +|"i" + +-- 1200 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= -2; +|"i" +[0] +[1] + +-- 1201 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= -1; +|"i" +[0] +[1] + +-- 1202 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= 0; +|"i" +[0] +[1] + +-- 1203 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= 1; +|"i" +[1] + +-- 1204 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i >= 2; +|"i" + +-- 1205 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i > -2; +|"i" +[0] +[1] + +-- 1206 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i > -1; +|"i" +[0] +[1] + +-- 1207 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i > 0; +|"i" +[1] + +-- 1208 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i > 1; +|"i" + +-- 1209 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i > 2; +|"i" + +-- 1210 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= -2; +|"i" + +-- 1211 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= -1; +|"i" + +-- 1212 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= 0; +|"i" +[0] + +-- 1213 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= 1; +|"i" +[0] +[1] + +-- 1214 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i <= 2; +|"i" +[0] +[1] + +-- 1215 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i < -2; +|"i" + +-- 1216 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i < -1; +|"i" + +-- 1217 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i < 0; +|"i" + +-- 1218 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i < 1; +|"i" +[0] + +-- 1219 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i < 2; +|"i" +[0] +[1] + +-- 1220 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i != -2; +|"i" +[0] +[1] + +-- 1221 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i != -1; +|"i" +[0] +[1] + +-- 1222 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i != 0; +|"i" +[1] + +-- 1223 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i != 1; +|"i" +[0] + +-- 1224 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i <= 1 && i != 2; +|"i" +[0] +[1] + +-- 1225 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i == -2; +|"i" + +-- 1226 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i == -1; +|"i" + +-- 1227 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i == 0; +|"i" +[0] + +-- 1228 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i == 1; +|"i" + +-- 1229 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i == 2; +|"i" + +-- 1230 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i >= -2; +|"i" +[0] + +-- 1231 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i >= -1; +|"i" +[0] + +-- 1232 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i >= 0; +|"i" +[0] + +-- 1233 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i >= 1; +|"i" + +-- 1234 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i >= 2; +|"i" + +-- 1235 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i > -2; +|"i" +[0] + +-- 1236 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i > -1; +|"i" +[0] + +-- 1237 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i > 0; +|"i" + +-- 1238 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i > 1; +|"i" + +-- 1239 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i > 2; +|"i" + +-- 1240 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i <= -2; +|"i" + +-- 1241 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i <= -1; +|"i" + +-- 1242 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i <= 0; +|"i" +[0] + +-- 1243 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i <= 1; +|"i" +[0] + +-- 1244 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i <= 2; +|"i" +[0] + +-- 1245 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i < -2; +|"i" + +-- 1246 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i < -1; +|"i" + +-- 1247 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i < 0; +|"i" + +-- 1248 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i < 1; +|"i" +[0] + +-- 1249 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i < 2; +|"i" +[0] + +-- 1250 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i != -2; +|"i" +[0] + +-- 1251 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i != -1; +|"i" +[0] + +-- 1252 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i != 0; +|"i" + +-- 1253 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i != 1; +|"i" +[0] + +-- 1254 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i > -1 && i < 1 && i != 2; +|"i" +[0] + +-- 1255 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == -2; +|"i" + +-- 1256 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == -1; +|"i" +[-1] + +-- 1257 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 0; +|"i" +[0] + +-- 1258 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 1; +|"i" + +-- 1259 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 2; +|"i" + +-- 1260 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == -2; +|"i" + +-- 1261 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == -1; +|"i" +[-1] + +-- 1262 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 0; +|"i" +[0] + +-- 1263 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 1; +|"i" + +-- 1264 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i == 2; +|"i" + +-- 1265 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= -2; +|"i" +[-1] +[0] + +-- 1266 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= -1; +|"i" +[-1] +[0] + +-- 1267 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= 0; +|"i" +[0] + +-- 1268 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= 1; +|"i" + +-- 1269 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i >= 2; +|"i" + +-- 1270 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i > -2; +|"i" +[-1] +[0] + +-- 1271 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i > -1; +|"i" +[0] + +-- 1272 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i > 0; +|"i" + +-- 1273 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i > 1; +|"i" + +-- 1274 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i > 2; +|"i" + +-- 1275 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= -2; +|"i" + +-- 1276 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= -1; +|"i" +[-1] + +-- 1277 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= 0; +|"i" +[-1] +[0] + + +-- 1278 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= 1; +|"i" +[-1] +[0] + +-- 1279 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i <= 2; +|"i" +[-1] +[0] + +-- 1280 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i < -2; +|"i" + +-- 1281 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i < -1; +|"i" + +-- 1282 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i < 0; +|"i" +[-1] + + +-- 1283 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i < 1; +|"i" +[-1] +[0] + +-- 1284 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i < 2; +|"i" +[-1] +[0] + +-- 1285 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i != -2; +|"i" +[-1] +[0] + +-- 1286 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i != -1; +|"i" +[0] + +-- 1287 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i != 0; +|"i" +[-1] + + +-- 1288 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i != 1; +|"i" +[-1] +[0] + +-- 1289 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i >= -1 && i < 1 && i != 2; +|"i" +[-1] +[0] + +-- 1290 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i == -2; +|"i" +[-2] + +-- 1291 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i == -1; +|"i" +[-1] + +-- 1292 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i == 0; +|"i" + + +-- 1293 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i == 1; +|"i" +[1] + +-- 1294 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i == 2; +|"i" +[2] + +-- 1295 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i >= -2; +|"i" +[-2] +[-1] +[1] +[2] + +-- 1296 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i >= -1; +|"i" +[-1] +[1] +[2] + +-- 1297 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i >= 0; +|"i" +[1] +[2] + + +-- 1298 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i >= 1; +|"i" +[1] +[2] + +-- 1299 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i >= 2; +|"i" +[2] + +-- 1300 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i > -2; +|"i" +[-1] +[1] +[2] + +-- 1301 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i > -1; +|"i" +[1] +[2] + +-- 1302 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i > 0; +|"i" +[1] +[2] + + +-- 1303 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i > 1; +|"i" +[2] + +-- 1304 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i > 2; +|"i" + +-- 1305 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i <= -2; +|"i" +[-2] + +-- 1306 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i <= -1; +|"i" +[-2] +[-1] + +-- 1307 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i <= 0; +|"i" +[-2] +[-1] + + +-- 1308 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i <= 1; +|"i" +[-2] +[-1] +[1] + +-- 1309 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i <= 2; +|"i" +[-2] +[-1] +[1] +[2] + +-- 1310 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i < -2; +|"i" + +-- 1311 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i < -1; +|"i" +[-2] + +-- 1312 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i < 0; +|"i" +[-2] +[-1] + + +-- 1313 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i < 1; +|"i" +[-2] +[-1] + +-- 1314 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i < 2; +|"i" +[-2] +[-1] +[1] + +-- 1315 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i != -2; +|"i" +[-1] +[1] +[2] + +-- 1316 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i != -1; +|"i" +[-2] +[1] +[2] + +-- 1317 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i != 0; +|"i" +[-2] +[-1] +[1] +[2] + + +-- 1318 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i != 1; +|"i" +[-2] +[-1] +[2] + +-- 1319 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX x ON t(i); + INSERT INTO t VALUES (1), (NULL), (-2), (0), (2), (-1); +COMMIT; +SELECT i FROM t WHERE i != 0 && i != 2; +|"i" +[-2] +[-1] +[1] + +-- 1320 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, -6), + (7, -8, 9), + (10, -11, -12), + (-13, 14, 15), + (-16, 17, -18), + (-19, -20, 21), + (-22, -23, -24); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 ORDER BY i, j; +|"i", "j", "k" +[1 2 3] +[4 5 -6] + +-- 1321 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + CREATE INDEX xi ON t(i); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, -6), + (7, -8, 9), + (10, -11, -12), + (-13, 14, 15), + (-16, 17, -18), + (-19, -20, 21), + (-22, -23, -24); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 ORDER BY i, j; +|"i", "j", "k" +[1 2 3] +[4 5 -6] + +-- 1322 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + CREATE INDEX xj ON t(j); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, -6), + (7, -8, 9), + (10, -11, -12), + (-13, 14, 15), + (-16, 17, -18), + (-19, -20, 21), + (-22, -23, -24); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 ORDER BY i, j; +|"i", "j", "k" +[1 2 3] +[4 5 -6] + +-- 1323 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + CREATE INDEX xi ON t(i); + CREATE INDEX xj ON t(j); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, -6), + (7, -8, 9), + (10, -11, -12), + (-13, 14, 15), + (-16, 17, -18), + (-19, -20, 21), + (-22, -23, -24); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 ORDER BY i, j; +|"i", "j", "k" +[1 2 3] +[4 5 -6] + +-- 1324 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + CREATE INDEX xi ON t(i); + CREATE INDEX xj ON t(j); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, -6), + (7, -8, 9), + (10, -11, -12), + (-13, 14, 15), + (-16, 17, -18), + (-19, -20, 21), + (-22, -23, -24); +COMMIT; +SELECT * FROM t WHERE j > 0 && i > 0 ORDER BY i, j; +|"i", "j", "k" +[1 2 3] +[4 5 -6] + +-- 1325 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, -6), + (7, -8, 9), + (10, -11, -12), + (-13, 14, 15), + (-16, 17, -18), + (-19, -20, 21), + (-22, -23, -24); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0; +|"i", "j", "k" +[1 2 3] + +-- 1326 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + CREATE INDEX xi ON t(i); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, -6), + (7, -8, 9), + (10, -11, -12), + (-13, 14, 15), + (-16, 17, -18), + (-19, -20, 21), + (-22, -23, -24); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0; +|"i", "j", "k" +[1 2 3] + +-- 1327 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + CREATE INDEX xj ON t(j); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, -6), + (7, -8, 9), + (10, -11, -12), + (-13, 14, 15), + (-16, 17, -18), + (-19, -20, 21), + (-22, -23, -24); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0; +|"i", "j", "k" +[1 2 3] + +-- 1328 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int); + CREATE INDEX xk ON t(k); + INSERT INTO t VALUES + (1, 2, 3), + (4, 5, -6), + (7, -8, 9), + (10, -11, -12), + (-13, 14, 15), + (-16, 17, -18), + (-19, -20, 21), + (-22, -23, -24); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0; +|"i", "j", "k" +[1 2 3] + +-- 1329 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int, l int); + INSERT INTO t VALUES + (1, 2, 3, 25), + (4, 5, -6, -26), + (7, -8, 9, 27), + (10, -11, -12, -28), + (-13, 14, 15, 29), + (-16, 17, -18, -30), + (-19, -20, 21, 31), + (-22, -23, -24, -32); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +|"i", "j", "k", "l" +[1 2 3 25] + +-- 1330 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int, l int); + CREATE INDEX xi ON t(i); + INSERT INTO t VALUES + (1, 2, 3, 25), + (4, 5, -6, -26), + (7, -8, 9, 27), + (10, -11, -12, -28), + (-13, 14, 15, 29), + (-16, 17, -18, -30), + (-19, -20, 21, 31), + (-22, -23, -24, -32); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +|"i", "j", "k", "l" +[1 2 3 25] + +-- 1331 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int, l int); + CREATE INDEX xj ON t(j); + INSERT INTO t VALUES + (1, 2, 3, 25), + (4, 5, -6, -26), + (7, -8, 9, 27), + (10, -11, -12, -28), + (-13, 14, 15, 29), + (-16, 17, -18, -30), + (-19, -20, 21, 31), + (-22, -23, -24, -32); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +|"i", "j", "k", "l" +[1 2 3 25] + +-- 1332 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int, l int); + CREATE INDEX xk ON t(k); + INSERT INTO t VALUES + (1, 2, 3, 25), + (4, 5, -6, -26), + (7, -8, 9, 27), + (10, -11, -12, -28), + (-13, 14, 15, 29), + (-16, 17, -18, -30), + (-19, -20, 21, 31), + (-22, -23, -24, -32); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +|"i", "j", "k", "l" +[1 2 3 25] + +-- 1333 +BEGIN TRANSACTION; + CREATE TABLE t (i int, j int, k int, l int); + CREATE INDEX xl ON t(l); + INSERT INTO t VALUES + (1, 2, 3, 25), + (4, 5, -6, -26), + (7, -8, 9, 27), + (10, -11, -12, -28), + (-13, 14, 15, 29), + (-16, 17, -18, -30), + (-19, -20, 21, 31), + (-22, -23, -24, -32); +COMMIT; +SELECT * FROM t WHERE i > 0 && j > 0 && k > 0 && l > 0; +|"i", "j", "k", "l" +[1 2 3 25] + +-- 1334 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (13), (15), (11), (16), (12), (14); +COMMIT; +SELECT * FROM t WHERE i > 12 && i BETWEEN 10 AND 20 AND i < 15 ORDER BY i; +|"i" +[13] +[14] + +-- 1335 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + CREATE INDEX xt_i ON t(i); + INSERT INTO t VALUES (13), (15), (11), (16), (12), (14); +COMMIT; +SELECT * FROM t WHERE i > 12 && i BETWEEN 10 AND 20 AND i < 42; +|"i" +[13] +[14] +[15] +[16] + +-- 1336 // https://github.com/cznic/ql/issues/102 +BEGIN TRANSACTION; + CREATE TABLE t (i byte); + INSERT INTO t VALUES (NULL); +COMMIT; +SELECT * FROM t; +|"i" +[] + +-- 1337 // https://github.com/cznic/ql/issues/103 +BEGIN TRANSACTION; + CREATE TABLE t (t time); + INSERT INTO t VALUES (date(2015, 6, 11, 11, 7, 50, 0, "UTC")); + CREATE INDEX x ON t(t); +COMMIT; +SELECT * FROM t; +|"t" +[2015-06-11 11:07:50 +0000 UTC] + +-- 1338 +BEGIN TRANSACTION; + CREATE TABLE t (t time); +COMMIT; +SELECT len(*) FROM t; +||invalid expression + +-- 1339 +BEGIN TRANSACTION; + CREATE TABLE t (t time); +COMMIT; +SELECT t.count(*) FROM t; +||invalid expression + +-- 1339 +BEGIN TRANSACTION; + CREATE TABLE t (t time); +COMMIT; +SELECT count(*) FROM t; +|"" +[0] + +-- 1340 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (1), (NULL), (3); +COMMIT; +SELECT count(*) FROM t; +|"" +[3] + +-- 1341 +BEGIN TRANSACTION; + CREATE TABLE t (i int); + INSERT INTO t VALUES (1), (NULL), (3); +COMMIT; +SELECT count() FROM t; +|"" +[3] diff --git a/Godeps/_workspace/src/github.com/cznic/sortutil/AUTHORS b/Godeps/_workspace/src/github.com/cznic/sortutil/AUTHORS new file mode 100644 index 000000000..0078f5f5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/sortutil/AUTHORS @@ -0,0 +1,11 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/sortutil/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/sortutil/CONTRIBUTORS new file mode 100644 index 000000000..9c9a5dd84 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/sortutil/CONTRIBUTORS @@ -0,0 +1,10 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Gary Burd +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/sortutil/LICENSE b/Godeps/_workspace/src/github.com/cznic/sortutil/LICENSE new file mode 100644 index 000000000..67983e0e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/sortutil/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The sortutil Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/sortutil/Makefile b/Godeps/_workspace/src/github.com/cznic/sortutil/Makefile new file mode 100644 index 000000000..2dc62d186 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/sortutil/Makefile @@ -0,0 +1,35 @@ +# Copyright 2014 The sortutil Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all clean editor later nuke todo + +grep=--include=*.go + +all: editor + go vet + golint . + make todo + +clean: + go clean + rm -f *~ + +editor: + gofmt -s -l -w *.go + go test -i + go test + go build + +later: + @grep -n $(grep) LATER * || true + @grep -n $(grep) MAYBE * || true + +nuke: clean + go clean -i + +todo: + @grep -nr $(grep) ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* * || true + @grep -nr $(grep) TODO * || true + @grep -nr $(grep) BUG * || true + @grep -nr $(grep) println * || true diff --git a/Godeps/_workspace/src/github.com/cznic/sortutil/README b/Godeps/_workspace/src/github.com/cznic/sortutil/README new file mode 100644 index 000000000..84ef92ccd --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/sortutil/README @@ -0,0 +1,4 @@ +Packages in this repository: + +Install: $ go get github.com/cznic/sortutil +Godocs: http://godoc.org/github.com/cznic/sortutil diff --git a/Godeps/_workspace/src/github.com/cznic/sortutil/all_test.go b/Godeps/_workspace/src/github.com/cznic/sortutil/all_test.go new file mode 100644 index 000000000..efd10b880 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/sortutil/all_test.go @@ -0,0 +1,407 @@ +// Copyright 2014 The sortutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sortutil + +import ( + "fmt" + "math" + "math/big" + "path" + "runtime" + "sort" + "strings" + "testing" + + "github.com/cznic/mathutil" +) + +func dbg(s string, va ...interface{}) { + if s == "" { + s = strings.Repeat("%v ", len(va)) + } + _, fn, fl, _ := runtime.Caller(1) + fmt.Printf("dbg %s:%d: ", path.Base(fn), fl) + fmt.Printf(s, va...) + fmt.Println() +} + +func caller(s string, va ...interface{}) { + _, fn, fl, _ := runtime.Caller(2) + fmt.Printf("caller: %s:%d: ", path.Base(fn), fl) + fmt.Printf(s, va...) + fmt.Println() + _, fn, fl, _ = runtime.Caller(1) + fmt.Printf("\tcallee: %s:%d: ", path.Base(fn), fl) + fmt.Println() +} + +func use(...interface{}) {} + +func TestBigIntSlice(t *testing.T) { + const N = 1e4 + s := make(BigIntSlice, N) + for i := range s { + s[i] = big.NewInt(int64(i ^ 0x55)) + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchBigInts(t *testing.T) { + const N = 1e1 + s := make(BigIntSlice, N) + for i := range s { + s[i] = big.NewInt(int64(2 * i)) + } + if g, e := SearchBigInts(s, big.NewInt(12)), 6; g != e { + t.Fatal(g, e) + } +} + +func TestBigRatSlice(t *testing.T) { + const N = 1e4 + s := make(BigRatSlice, N) + for i := range s { + s[i] = big.NewRat(int64(i^0x55), 1) + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchBigRats(t *testing.T) { + const N = 1e1 + s := make(BigRatSlice, N) + for i := range s { + s[i] = big.NewRat(int64(2*i), 1) + } + if g, e := SearchBigRats(s, big.NewRat(12, 1)), 6; g != e { + t.Fatal(g, e) + } +} + +func TestByteSlice(t *testing.T) { + const N = 1e4 + s := make(ByteSlice, N) + for i := range s { + s[i] = byte(i) ^ 0x55 + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchBytes(t *testing.T) { + const N = 1e1 + s := make(ByteSlice, N) + for i := range s { + s[i] = byte(2 * i) + } + if g, e := SearchBytes(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestFloat32Slice(t *testing.T) { + const N = 1e4 + s := make(Float32Slice, N) + for i := range s { + s[i] = float32(i ^ 0x55aa55aa) + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchFloat32s(t *testing.T) { + const N = 1e4 + s := make(Float32Slice, N) + for i := range s { + s[i] = float32(2 * i) + } + if g, e := SearchFloat32s(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestInt8Slice(t *testing.T) { + const N = 1e4 + s := make(Int8Slice, N) + for i := range s { + s[i] = int8(i) ^ 0x55 + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchInt8s(t *testing.T) { + const N = 1e1 + s := make(Int8Slice, N) + for i := range s { + s[i] = int8(2 * i) + } + if g, e := SearchInt8s(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestInt16Slice(t *testing.T) { + const N = 1e4 + s := make(Int16Slice, N) + for i := range s { + s[i] = int16(i) ^ 0x55aa + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchInt16s(t *testing.T) { + const N = 1e4 + s := make(Int16Slice, N) + for i := range s { + s[i] = int16(2 * i) + } + if g, e := SearchInt16s(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestInt32Slice(t *testing.T) { + const N = 1e4 + s := make(Int32Slice, N) + for i := range s { + s[i] = int32(i) ^ 0x55aa55aa + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchInt32s(t *testing.T) { + const N = 1e4 + s := make(Int32Slice, N) + for i := range s { + s[i] = int32(2 * i) + } + if g, e := SearchInt32s(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestInt64Slice(t *testing.T) { + const N = 1e4 + s := make(Int64Slice, N) + for i := range s { + s[i] = int64(i) ^ 0x55aa55aa55aa55aa + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchInt64s(t *testing.T) { + const N = 1e4 + s := make(Int64Slice, N) + for i := range s { + s[i] = int64(2 * i) + } + if g, e := SearchInt64s(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestUintSlice(t *testing.T) { + const N = 1e4 + s := make(UintSlice, N) + for i := range s { + s[i] = uint(i) ^ 0x55aa55aa + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchUints(t *testing.T) { + const N = 1e4 + s := make(UintSlice, N) + for i := range s { + s[i] = uint(2 * i) + } + if g, e := SearchUints(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestUint16Slice(t *testing.T) { + const N = 1e4 + s := make(Uint16Slice, N) + for i := range s { + s[i] = uint16(i) ^ 0x55aa + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchUint16s(t *testing.T) { + const N = 1e4 + s := make(Uint16Slice, N) + for i := range s { + s[i] = uint16(2 * i) + } + if g, e := SearchUint16s(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestUint32Slice(t *testing.T) { + const N = 1e4 + s := make(Uint32Slice, N) + for i := range s { + s[i] = uint32(i) ^ 0x55aa55aa + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchUint32s(t *testing.T) { + const N = 1e4 + s := make(Uint32Slice, N) + for i := range s { + s[i] = uint32(2 * i) + } + if g, e := SearchUint32s(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestUint64Slice(t *testing.T) { + const N = 1e4 + s := make(Uint64Slice, N) + for i := range s { + s[i] = uint64(i) ^ 0x55aa55aa55aa55aa + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchUint64s(t *testing.T) { + const N = 1e4 + s := make(Uint64Slice, N) + for i := range s { + s[i] = uint64(2 * i) + } + if g, e := SearchUint64s(s, 12), 6; g != e { + t.Fatal(g, e) + } +} + +func TestRuneSlice(t *testing.T) { + const N = 1e4 + s := make(RuneSlice, N) + for i := range s { + s[i] = rune(i ^ 0x55aa55aa) + } + s.Sort() + if !sort.IsSorted(s) { + t.Fatal(false) + } +} + +func TestSearchRunes(t *testing.T) { + const N = 1e4 + s := make(RuneSlice, N) + for i := range s { + s[i] = rune(2 * i) + } + if g, e := SearchRunes(s, rune('\x0c')), 6; g != e { + t.Fatal(g, e) + } +} + +func dedupe(a []int) (r []int) { + a = append([]int(nil), a...) + if len(a) < 2 { + return a + } + + sort.Ints(a) + if a[0] < 0 { + panic("internal error") + } + + last := -1 + for _, v := range a { + if v != last { + r = append(r, v) + last = v + } + } + return r +} + +func TestDedup(t *testing.T) { + a := []int{} + n := Dedupe(sort.IntSlice(a)) + if g, e := n, 0; g != e { + t.Fatal(g, e) + } + + if g, e := len(a), 0; g != e { + t.Fatal(g, e) + } + + for c := 1; c <= 7; c++ { + in := make([]int, c) + lim := int(mathutil.ModPowUint32(uint32(c), uint32(c), math.MaxUint32)) + for n := 0; n < lim; n++ { + m := n + for i := range in { + in[i] = m % c + m /= c + } + in0 := append([]int(nil), in...) + out0 := dedupe(in) + n := Dedupe(sort.IntSlice(in)) + if g, e := n, len(out0); g != e { + t.Fatalf("n %d, exp %d, in0 %v, in %v, out0 %v", g, e, in0, in, out0) + } + + for i, v := range out0 { + if g, e := in[i], v; g != e { + t.Fatalf("n %d, in0 %v, in %v, out0 %v", n, in0, in, out0) + } + } + } + } +} + +func ExampleDedupe() { + a := []int{4, 1, 2, 1, 3, 4, 2} + fmt.Println(a[:Dedupe(sort.IntSlice(a))]) + + b := []string{"foo", "bar", "baz", "bar", "foo", "qux", "qux"} + fmt.Println(b[:Dedupe(sort.StringSlice(b))]) + // Output: + // [1 2 3 4] + // [bar baz foo qux] +} diff --git a/Godeps/_workspace/src/github.com/cznic/sortutil/sortutil.go b/Godeps/_workspace/src/github.com/cznic/sortutil/sortutil.go new file mode 100644 index 000000000..132d3546e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/sortutil/sortutil.go @@ -0,0 +1,271 @@ +// Copyright 2014 The sortutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package sortutil provides utilities supplementing the standard 'sort' package. +// +// Changelog +// +// 2015-06-17: Added utils for math/big.{Int,Rat}. +package sortutil + +import ( + "math/big" +) + +import "sort" + +// BigIntSlice attaches the methods of sort.Interface to []*big.Int, sorting in increasing order. +type BigIntSlice []*big.Int + +func (s BigIntSlice) Len() int { return len(s) } +func (s BigIntSlice) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } +func (s BigIntSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s BigIntSlice) Sort() { + sort.Sort(s) +} + +// SearchBigInts searches for x in a sorted slice of *big.Int and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchBigInts(a []*big.Int, x *big.Int) int { + return sort.Search(len(a), func(i int) bool { return a[i].Cmp(x) >= 0 }) +} + +// BigRatSlice attaches the methods of sort.Interface to []*big.Rat, sorting in increasing order. +type BigRatSlice []*big.Rat + +func (s BigRatSlice) Len() int { return len(s) } +func (s BigRatSlice) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } +func (s BigRatSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s BigRatSlice) Sort() { + sort.Sort(s) +} + +// SearchBigRats searches for x in a sorted slice of *big.Int and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchBigRats(a []*big.Rat, x *big.Rat) int { + return sort.Search(len(a), func(i int) bool { return a[i].Cmp(x) >= 0 }) +} + +// ByteSlice attaches the methods of sort.Interface to []byte, sorting in increasing order. +type ByteSlice []byte + +func (s ByteSlice) Len() int { return len(s) } +func (s ByteSlice) Less(i, j int) bool { return s[i] < s[j] } +func (s ByteSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s ByteSlice) Sort() { + sort.Sort(s) +} + +// SearchBytes searches for x in a sorted slice of bytes and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchBytes(a []byte, x byte) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// Float32Slice attaches the methods of sort.Interface to []float32, sorting in increasing order. +type Float32Slice []float32 + +func (s Float32Slice) Len() int { return len(s) } +func (s Float32Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s Float32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s Float32Slice) Sort() { + sort.Sort(s) +} + +// SearchFloat32s searches for x in a sorted slice of float32 and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchFloat32s(a []float32, x float32) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// Int8Slice attaches the methods of sort.Interface to []int8, sorting in increasing order. +type Int8Slice []int8 + +func (s Int8Slice) Len() int { return len(s) } +func (s Int8Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s Int8Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s Int8Slice) Sort() { + sort.Sort(s) +} + +// SearchInt8s searches for x in a sorted slice of int8 and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchInt8s(a []int8, x int8) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// Int16Slice attaches the methods of sort.Interface to []int16, sorting in increasing order. +type Int16Slice []int16 + +func (s Int16Slice) Len() int { return len(s) } +func (s Int16Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s Int16Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s Int16Slice) Sort() { + sort.Sort(s) +} + +// SearchInt16s searches for x in a sorted slice of int16 and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchInt16s(a []int16, x int16) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// Int32Slice attaches the methods of sort.Interface to []int32, sorting in increasing order. +type Int32Slice []int32 + +func (s Int32Slice) Len() int { return len(s) } +func (s Int32Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s Int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s Int32Slice) Sort() { + sort.Sort(s) +} + +// SearchInt32s searches for x in a sorted slice of int32 and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchInt32s(a []int32, x int32) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order. +type Int64Slice []int64 + +func (s Int64Slice) Len() int { return len(s) } +func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s Int64Slice) Sort() { + sort.Sort(s) +} + +// SearchInt64s searches for x in a sorted slice of int64 and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchInt64s(a []int64, x int64) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// UintSlice attaches the methods of sort.Interface to []uint, sorting in increasing order. +type UintSlice []uint + +func (s UintSlice) Len() int { return len(s) } +func (s UintSlice) Less(i, j int) bool { return s[i] < s[j] } +func (s UintSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s UintSlice) Sort() { + sort.Sort(s) +} + +// SearchUints searches for x in a sorted slice of uints and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchUints(a []uint, x uint) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// Uint16Slice attaches the methods of sort.Interface to []uint16, sorting in increasing order. +type Uint16Slice []uint16 + +func (s Uint16Slice) Len() int { return len(s) } +func (s Uint16Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s Uint16Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s Uint16Slice) Sort() { + sort.Sort(s) +} + +// SearchUint16s searches for x in a sorted slice of uint16 and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchUint16s(a []uint16, x uint16) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// Uint32Slice attaches the methods of sort.Interface to []uint32, sorting in increasing order. +type Uint32Slice []uint32 + +func (s Uint32Slice) Len() int { return len(s) } +func (s Uint32Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s Uint32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s Uint32Slice) Sort() { + sort.Sort(s) +} + +// SearchUint32s searches for x in a sorted slice of uint32 and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchUint32s(a []uint32, x uint32) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// Uint64Slice attaches the methods of sort.Interface to []uint64, sorting in increasing order. +type Uint64Slice []uint64 + +func (s Uint64Slice) Len() int { return len(s) } +func (s Uint64Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s Uint64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s Uint64Slice) Sort() { + sort.Sort(s) +} + +// SearchUint64s searches for x in a sorted slice of uint64 and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchUint64s(a []uint64, x uint64) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// RuneSlice attaches the methods of sort.Interface to []rune, sorting in increasing order. +type RuneSlice []rune + +func (s RuneSlice) Len() int { return len(s) } +func (s RuneSlice) Less(i, j int) bool { return s[i] < s[j] } +func (s RuneSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Sort is a convenience method. +func (s RuneSlice) Sort() { + sort.Sort(s) +} + +// SearchRunes searches for x in a sorted slice of uint64 and returns the index +// as specified by sort.Search. The slice must be sorted in ascending order. +func SearchRunes(a []rune, x rune) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +// Dedupe returns n, the number of distinct elements in data. The resulting +// elements are sorted in elements [0, n) or data[:n] for a slice. +func Dedupe(data sort.Interface) (n int) { + if n = data.Len(); n < 2 { + return n + } + + sort.Sort(data) + a, b := 0, 1 + for b < n { + if data.Less(a, b) { + a++ + if a != b { + data.Swap(a, b) + } + } + b++ + } + return a + 1 +} diff --git a/Godeps/_workspace/src/github.com/cznic/strutil/AUTHORS b/Godeps/_workspace/src/github.com/cznic/strutil/AUTHORS new file mode 100644 index 000000000..d04c45011 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/strutil/AUTHORS @@ -0,0 +1,12 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +CZ.NIC z.s.p.o. +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/strutil/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/strutil/CONTRIBUTORS new file mode 100644 index 000000000..5e86f0607 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/strutil/CONTRIBUTORS @@ -0,0 +1,9 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> diff --git a/Godeps/_workspace/src/github.com/cznic/strutil/LICENSE b/Godeps/_workspace/src/github.com/cznic/strutil/LICENSE new file mode 100644 index 000000000..2fdd92cf7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/strutil/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The strutil Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/strutil/Makefile b/Godeps/_workspace/src/github.com/cznic/strutil/Makefile new file mode 100644 index 000000000..03ccc39e0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/strutil/Makefile @@ -0,0 +1,50 @@ +# Copyright (c) 2014 The sortutil Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all clean cover cpu editor internalError later mem nuke todo + +grep=--include=*.go --include=*.l --include=*.y + +all: editor + go vet || true + golint || true + make todo + +clean: + go clean + rm -f *~ cpu.test mem.test + +cover: + t=$(shell tempfile) ; go test -coverprofile $$t && go tool cover -html $$t && unlink $$t + +cpu: + go test -c -o cpu.test + ./cpu.test -noerr -test.cpuprofile cpu.out + go tool pprof --lines cpu.test cpu.out + +editor: + gofmt -l -s -w *.go + go test + go install + +internalError: + egrep -ho '"internal error.*"' *.go | sort | cat -n + +later: + @grep -n $(grep) LATER * || true + @grep -n $(grep) MAYBE * || true + +mem: + go test -c -o mem.test + ./mem.test -test.bench . -test.memprofile mem.out + go tool pprof --lines --web --alloc_space mem.test mem.out + +nuke: clean + go clean -i + +todo: + @grep -nr $(grep) ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* * || true + @grep -nr $(grep) TODO * || true + @grep -nr $(grep) BUG * || true + @grep -nr $(grep) [^[:alpha:]]println * || true diff --git a/Godeps/_workspace/src/github.com/cznic/strutil/README b/Godeps/_workspace/src/github.com/cznic/strutil/README new file mode 100644 index 000000000..a8652a3dc --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/strutil/README @@ -0,0 +1,8 @@ +This is a goinstall-able mirror of modified code already published at: +http://git.nic.cz/redmine/projects/gostrutil/repository + +Online godoc documentation for this package (should be) available at: +http://gopkgdoc.appspot.com/pkg/github.com/cznic/strutil + +Installation: +$ go get github.com/cznic/strutil diff --git a/Godeps/_workspace/src/github.com/cznic/strutil/all_test.go b/Godeps/_workspace/src/github.com/cznic/strutil/all_test.go new file mode 100644 index 000000000..b47ca4d7c --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/strutil/all_test.go @@ -0,0 +1,227 @@ +// Copyright (c) 2014 The sortutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package strutil + +import ( + "bytes" + "fmt" + "github.com/cznic/mathutil" + "math" + "os" + "path" + "runtime" + "strings" + "testing" + "unsafe" +) + +func caller(s string, va ...interface{}) { + _, fn, fl, _ := runtime.Caller(2) + fmt.Fprintf(os.Stderr, "caller: %s:%d: ", path.Base(fn), fl) + fmt.Fprintf(os.Stderr, s, va...) + fmt.Fprintln(os.Stderr) + _, fn, fl, _ = runtime.Caller(1) + fmt.Fprintf(os.Stderr, "\tcallee: %s:%d: ", path.Base(fn), fl) + fmt.Fprintln(os.Stderr) +} + +func dbg(s string, va ...interface{}) { + if s == "" { + s = strings.Repeat("%v ", len(va)) + } + _, fn, fl, _ := runtime.Caller(1) + fmt.Fprintf(os.Stderr, "dbg %s:%d: ", path.Base(fn), fl) + fmt.Fprintf(os.Stderr, s, va...) + fmt.Fprintln(os.Stderr) +} + +func TODO(...interface{}) string { + _, fn, fl, _ := runtime.Caller(1) + return fmt.Sprintf("TODO: %s:%d:\n", path.Base(fn), fl) +} + +func use(...interface{}) {} +func TestBase64(t *testing.T) { + const max = 768 + r, err := mathutil.NewFC32(math.MinInt32, math.MaxInt32, true) + if err != nil { + t.Fatal(err) + } + + bin := []byte{} + for i := 0; i < max; i++ { + bin = append(bin, byte(r.Next())) + cmp, err := Base64Decode(Base64Encode(bin)) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(bin, cmp) { + t.Fatalf("a: % x\nb: % x", bin, cmp) + } + } +} + +func TestBase32Ext(t *testing.T) { + const max = 640 + r, err := mathutil.NewFC32(math.MinInt32, math.MaxInt32, true) + if err != nil { + t.Fatal(err) + } + + bin := []byte{} + for i := 0; i < max; i++ { + bin = append(bin, byte(r.Next())) + cmp, err := Base32ExtDecode(Base32ExtEncode(bin)) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(bin, cmp) { + t.Fatalf("a: % x\nb: % x", bin, cmp) + } + } +} + +func TestFields(t *testing.T) { + p := []string{"", "\\", "|", "0", "1", "2"} + one := func(n int) string { + s := "" + for i := 0; i < 3; i++ { + s += p[n%len(p)] + n /= len(p) + } + return s + } + max := len(p) * len(p) * len(p) + var a [3]string + for x := 0; x < max; x++ { + a[0] = one(x) + for x := 0; x < max; x++ { + a[1] = one(x) + for x := 0; x < len(p)*len(p); x++ { + a[2] = one(x) + enc := JoinFields(a[:], "|") + dec := SplitFields(enc, "|") + if g, e := strings.Join(dec, ","), strings.Join(a[:], ","); g != e { + t.Fatal(g, e) + } + } + } + } +} + +func ExamplePrettyString() { + type prettyStringType struct { + Array [3]int + Bool bool + Chan <-chan int + Complex128 complex128 + Complex64 complex64 + Float32 float32 + Float64 float64 + Func func() + Func2 interface{} + Func3 interface{} + Int int + Int16 int16 + Int32 int32 + Int64 int64 + Int8 int8 + Interface interface{} + Map map[int]string + Ptr *int + Slice []int + String string + Struct *prettyStringType + Struct2 *prettyStringType + Uint uint + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + Uint8 byte + Uintptr uintptr + UnsafePointer unsafe.Pointer + } + + i := 314 + v := &prettyStringType{ + Array: [...]int{10, 20, 30}, + Bool: true, + Chan: make(<-chan int, 100), + Complex128: 3 - 4i, + Complex64: 1 + 2i, + Float32: 1.5, + Float64: 3.5, + Func2: func(a, b, c int, z ...string) (d, e, f string) { return }, + Func3: func(a, b, c int, z ...string) {}, + Func: func() {}, + Int16: -44, + Int32: -45, + Int64: -46, + Int8: -43, + Int: -42, + Map: map[int]string{100: "100", 200: "200", 300: "300"}, + Ptr: &i, + Slice: []int{10, 20, 30}, + String: "foo", + Struct: &prettyStringType{Int: 8888}, + Struct2: &prettyStringType{}, + Uint16: 44, + Uint32: 45, + Uint64: 46, + Uint8: 43, + Uint: 42, + Uintptr: uintptr(99), + UnsafePointer: unsafe.Pointer(uintptr(0x12345678)), + } + v.Interface = v + fmt.Println(PrettyString(v, "", "", nil)) + // Output: + // &strutil.prettyStringType{ + // · Array: [3]int{ + // · · 0: 10, + // · · 1: 20, + // · · 2: 30, + // · }, + // · Bool: true, + // · Chan: <-chan int// capacity: 100, + // · Complex128: (3-4i), + // · Complex64: (1+2i), + // · Float32: 1.5, + // · Float64: 3.5, + // · Func: func() { ... }, + // · Func2: func(int, int, int, ...string) (string, string, string) { ... }, + // · Func3: func(int, int, int, ...string) { ... }, + // · Int: -42, + // · Int16: -44, + // · Int32: -45, + // · Int64: -46, + // · Int8: -43, + // · Interface: &strutil.prettyStringType{ /* recursive/repetitive pointee not shown */ }, + // · Map: map[int]string{ + // · · 100: "100", + // · · 200: "200", + // · · 300: "300", + // · }, + // · Ptr: &314, + // · Slice: []int{ // len 3 + // · · 0: 10, + // · · 1: 20, + // · · 2: 30, + // · }, + // · String: "foo", + // · Struct: &strutil.prettyStringType{ + // · · Int: 8888, + // · }, + // · Uint: 42, + // · Uint16: 44, + // · Uint32: 45, + // · Uint64: 46, + // · Uint8: 43, + // · Uintptr: 99, + // · UnsafePointer: 0x12345678, + // } +} diff --git a/Godeps/_workspace/src/github.com/cznic/strutil/strutil.go b/Godeps/_workspace/src/github.com/cznic/strutil/strutil.go new file mode 100644 index 000000000..9876e1794 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/strutil/strutil.go @@ -0,0 +1,645 @@ +// Copyright (c) 2014 The sortutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package strutil collects utils supplemental to the standard strings package. +package strutil + +import ( + "bytes" + "encoding/base32" + "encoding/base64" + "fmt" + "io" + "reflect" + "sort" + "strconv" + "strings" + "sync" +) + +// Base32ExtDecode decodes base32 extended (RFC 4648) text to binary data. +func Base32ExtDecode(text []byte) (data []byte, err error) { + n := base32.HexEncoding.DecodedLen(len(text)) + data = make([]byte, n) + decoder := base32.NewDecoder(base32.HexEncoding, bytes.NewBuffer(text)) + if n, err = decoder.Read(data); err != nil { + n = 0 + } + data = data[:n] + return +} + +// Base32ExtEncode encodes binary data to base32 extended (RFC 4648) encoded text. +func Base32ExtEncode(data []byte) (text []byte) { + n := base32.HexEncoding.EncodedLen(len(data)) + buf := bytes.NewBuffer(make([]byte, 0, n)) + encoder := base32.NewEncoder(base32.HexEncoding, buf) + encoder.Write(data) + encoder.Close() + if buf.Len() != n { + panic("internal error") + } + return buf.Bytes() +} + +// Base64Decode decodes base64 text to binary data. +func Base64Decode(text []byte) (data []byte, err error) { + n := base64.StdEncoding.DecodedLen(len(text)) + data = make([]byte, n) + decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewBuffer(text)) + if n, err = decoder.Read(data); err != nil { + n = 0 + } + data = data[:n] + return +} + +// Base64Encode encodes binary data to base64 encoded text. +func Base64Encode(data []byte) (text []byte) { + n := base64.StdEncoding.EncodedLen(len(data)) + buf := bytes.NewBuffer(make([]byte, 0, n)) + encoder := base64.NewEncoder(base64.StdEncoding, buf) + encoder.Write(data) + encoder.Close() + if buf.Len() != n { + panic("internal error") + } + return buf.Bytes() +} + +// Formatter is an io.Writer extended by a fmt.Printf like function Format +type Formatter interface { + io.Writer + Format(format string, args ...interface{}) (n int, errno error) +} + +type indentFormatter struct { + io.Writer + indent []byte + indentLevel int + state int +} + +const ( + st0 = iota + stBOL + stPERC + stBOLPERC +) + +// IndentFormatter returns a new Formatter which interprets %i and %u in the +// Format() format string as indent and undent commands. The commands can +// nest. The Formatter writes to io.Writer 'w' and inserts one 'indent' +// string per current indent level value. +// Behaviour of commands reaching negative indent levels is undefined. +// IndentFormatter(os.Stdout, "\t").Format("abc%d%%e%i\nx\ny\n%uz\n", 3) +// output: +// abc3%e +// x +// y +// z +// The Go quoted string literal form of the above is: +// "abc%%e\n\tx\n\tx\nz\n" +// The commands can be scattered between separate invocations of Format(), +// i.e. the formatter keeps track of the indent level and knows if it is +// positioned on start of a line and should emit indentation(s). +// The same output as above can be produced by e.g.: +// f := IndentFormatter(os.Stdout, " ") +// f.Format("abc%d%%e%i\nx\n", 3) +// f.Format("y\n%uz\n") +func IndentFormatter(w io.Writer, indent string) Formatter { + return &indentFormatter{w, []byte(indent), 0, stBOL} +} + +func (f *indentFormatter) format(flat bool, format string, args ...interface{}) (n int, errno error) { + buf := []byte{} + for i := 0; i < len(format); i++ { + c := format[i] + switch f.state { + case st0: + switch c { + case '\n': + cc := c + if flat && f.indentLevel != 0 { + cc = ' ' + } + buf = append(buf, cc) + f.state = stBOL + case '%': + f.state = stPERC + default: + buf = append(buf, c) + } + case stBOL: + switch c { + case '\n': + cc := c + if flat && f.indentLevel != 0 { + cc = ' ' + } + buf = append(buf, cc) + case '%': + f.state = stBOLPERC + default: + if !flat { + for i := 0; i < f.indentLevel; i++ { + buf = append(buf, f.indent...) + } + } + buf = append(buf, c) + f.state = st0 + } + case stBOLPERC: + switch c { + case 'i': + f.indentLevel++ + f.state = stBOL + case 'u': + f.indentLevel-- + f.state = stBOL + default: + if !flat { + for i := 0; i < f.indentLevel; i++ { + buf = append(buf, f.indent...) + } + } + buf = append(buf, '%', c) + f.state = st0 + } + case stPERC: + switch c { + case 'i': + f.indentLevel++ + f.state = st0 + case 'u': + f.indentLevel-- + f.state = st0 + default: + buf = append(buf, '%', c) + f.state = st0 + } + default: + panic("unexpected state") + } + } + switch f.state { + case stPERC, stBOLPERC: + buf = append(buf, '%') + } + return f.Write([]byte(fmt.Sprintf(string(buf), args...))) +} + +func (f *indentFormatter) Format(format string, args ...interface{}) (n int, errno error) { + return f.format(false, format, args...) +} + +type flatFormatter indentFormatter + +// FlatFormatter returns a newly created Formatter with the same functionality as the one returned +// by IndentFormatter except it allows a newline in the 'format' string argument of Format +// to pass through iff indent level is currently zero. +// +// If indent level is non-zero then such new lines are changed to a space character. +// There is no indent string, the %i and %u format verbs are used solely to determine the indent level. +// +// The FlatFormatter is intended for flattening of normally nested structure textual representation to +// a one top level structure per line form. +// FlatFormatter(os.Stdout, " ").Format("abc%d%%e%i\nx\ny\n%uz\n", 3) +// output in the form of a Go quoted string literal: +// "abc3%%e x y z\n" +func FlatFormatter(w io.Writer) Formatter { + return (*flatFormatter)(IndentFormatter(w, "").(*indentFormatter)) +} + +func (f *flatFormatter) Format(format string, args ...interface{}) (n int, errno error) { + return (*indentFormatter)(f).format(true, format, args...) +} + +// Pool handles aligning of strings having equal values to the same string instance. +// Intended use is to conserve some memory e.g. where a large number of identically valued strings +// with non identical backing arrays may exists in several semantically distinct instances of some structs. +// Pool is *not* concurrent access safe. It doesn't handle common prefix/suffix aligning, +// e.g. having s1 == "abc" and s2 == "bc", s2 is not automatically aligned as s1[1:]. +type Pool struct { + pool map[string]string +} + +// NewPool returns a newly created Pool. +func NewPool() *Pool { + return &Pool{map[string]string{}} +} + +// Align returns a string with the same value as its argument. It guarantees that +// all aligned strings share a single instance in memory. +func (p *Pool) Align(s string) string { + if a, ok := p.pool[s]; ok { + return a + } + + s = StrPack(s) + p.pool[s] = s + return s +} + +// Count returns the number of items in the pool. +func (p *Pool) Count() int { + return len(p.pool) +} + +// GoPool is a concurrent access safe version of Pool. +type GoPool struct { + pool map[string]string + rwm *sync.RWMutex +} + +// NewGoPool returns a newly created GoPool. +func NewGoPool() (p *GoPool) { + return &GoPool{map[string]string{}, &sync.RWMutex{}} +} + +// Align returns a string with the same value as its argument. It guarantees that +// all aligned strings share a single instance in memory. +func (p *GoPool) Align(s string) (y string) { + if s != "" { + p.rwm.RLock() // R++ + if a, ok := p.pool[s]; ok { // found + p.rwm.RUnlock() // R-- + return a + } + + p.rwm.RUnlock() // R-- + // not found but with a race condition, retry within a write lock + p.rwm.Lock() // W++ + defer p.rwm.Unlock() // W-- + if a, ok := p.pool[s]; ok { // done in a race + return a + } + + // we won + s = StrPack(s) + p.pool[s] = s + return s + } + + return +} + +// Count returns the number of items in the pool. +func (p *GoPool) Count() int { + return len(p.pool) +} + +// Dict is a string <-> id bijection. Dict is *not* concurrent access safe for assigning new ids +// to strings not yet contained in the bijection. +// Id for an empty string is guaranteed to be 0, +// thus Id for any non empty string is guaranteed to be non zero. +type Dict struct { + si map[string]int + is []string +} + +// NewDict returns a newly created Dict. +func NewDict() (d *Dict) { + d = &Dict{map[string]int{}, []string{}} + d.Id("") + return +} + +// Count returns the number of items in the dict. +func (d *Dict) Count() int { + return len(d.is) +} + +// Id maps string s to its numeric identificator. +func (d *Dict) Id(s string) (y int) { + if y, ok := d.si[s]; ok { + return y + } + + s = StrPack(s) + y = len(d.is) + d.si[s] = y + d.is = append(d.is, s) + return +} + +// S maps an id to its string value and ok == true. Id values not contained in the bijection +// return "", false. +func (d *Dict) S(id int) (s string, ok bool) { + if id >= len(d.is) { + return "", false + } + return d.is[id], true +} + +// GoDict is a concurrent access safe version of Dict. +type GoDict struct { + si map[string]int + is []string + rwm *sync.RWMutex +} + +// NewGoDict returns a newly created GoDict. +func NewGoDict() (d *GoDict) { + d = &GoDict{map[string]int{}, []string{}, &sync.RWMutex{}} + d.Id("") + return +} + +// Count returns the number of items in the dict. +func (d *GoDict) Count() int { + return len(d.is) +} + +// Id maps string s to its numeric identificator. The implementation honors getting +// an existing id at the cost of assigning a new one. +func (d *GoDict) Id(s string) (y int) { + d.rwm.RLock() // R++ + if y, ok := d.si[s]; ok { // found + d.rwm.RUnlock() // R-- + return y + } + + d.rwm.RUnlock() // R-- + + // not found but with a race condition + d.rwm.Lock() // W++ recheck with write lock + defer d.rwm.Unlock() // W-- + if y, ok := d.si[s]; ok { // some other goroutine won already + return y + } + + // a race free not found state => insert the string + s = StrPack(s) + y = len(d.is) + d.si[s] = y + d.is = append(d.is, s) + return +} + +// S maps an id to its string value and ok == true. Id values not contained in the bijection +// return "", false. +func (d *GoDict) S(id int) (s string, ok bool) { + d.rwm.RLock() // R++ + defer d.rwm.RUnlock() // R-- + if id >= len(d.is) { + return "", false + } + return d.is[id], true +} + +// StrPack returns a new instance of s which is tightly packed in memory. +// It is intended for avoiding the situation where having a live reference +// to a string slice over an unreferenced biger underlying string keeps the biger one +// in memory anyway - it can't be GCed. +func StrPack(s string) string { + return string([]byte(s)) +} + +// JoinFields returns strings in flds joined by sep. Flds may contain arbitrary +// bytes, including the sep as they are safely escaped. JoinFields panics if +// sep is the backslash character or if len(sep) != 1. +func JoinFields(flds []string, sep string) string { + if len(sep) != 1 || sep == "\\" { + panic("invalid separator") + } + + a := make([]string, len(flds)) + for i, v := range flds { + v = strings.Replace(v, "\\", "\\0", -1) + a[i] = strings.Replace(v, sep, "\\1", -1) + } + return strings.Join(a, sep) +} + +// SplitFields splits s, which must be produced by JoinFields using the same +// sep, into flds. SplitFields panics if sep is the backslash character or if +// len(sep) != 1. +func SplitFields(s, sep string) (flds []string) { + if len(sep) != 1 || sep == "\\" { + panic("invalid separator") + } + + a := strings.Split(s, sep) + r := make([]string, len(a)) + for i, v := range a { + v = strings.Replace(v, "\\1", sep, -1) + r[i] = strings.Replace(v, "\\0", "\\", -1) + } + return r +} + +// PrettyPrintHooks allow to customize the result of PrettyPrint for types +// listed in the map value. +type PrettyPrintHooks map[reflect.Type]func(f Formatter, v interface{}, prefix, suffix string) + +// PrettyString returns the output of PrettyPrint as a string. +func PrettyString(v interface{}, prefix, suffix string, hooks PrettyPrintHooks) string { + var b bytes.Buffer + PrettyPrint(&b, v, prefix, suffix, hooks) + return b.String() +} + +// PrettyPrint pretty prints v to w. Zero values and unexported struct fields +// are omitted. +func PrettyPrint(w io.Writer, v interface{}, prefix, suffix string, hooks PrettyPrintHooks) { + if v == nil { + return + } + + f := IndentFormatter(w, "· ") + + defer func() { + if e := recover(); e != nil { + f.Format("\npanic: %v", e) + } + }() + + prettyPrint(nil, f, prefix, suffix, v, hooks) +} + +func prettyPrint(protect map[interface{}]struct{}, sf Formatter, prefix, suffix string, v interface{}, hooks PrettyPrintHooks) { + if v == nil { + return + } + + rt := reflect.TypeOf(v) + if handler := hooks[rt]; handler != nil { + handler(sf, v, prefix, suffix) + return + } + + rv := reflect.ValueOf(v) + switch rt.Kind() { + case reflect.Slice: + if rv.Len() == 0 { + return + } + + sf.Format("%s[]%T{ // len %d%i\n", prefix, rv.Index(0).Interface(), rv.Len()) + for i := 0; i < rv.Len(); i++ { + prettyPrint(protect, sf, fmt.Sprintf("%d: ", i), ",\n", rv.Index(i).Interface(), hooks) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%u}" + suffix) + case reflect.Array: + if reflect.Zero(rt).Interface() == rv.Interface() { + return + } + + sf.Format("%s[%d]%T{%i\n", prefix, rv.Len(), rv.Index(0).Interface()) + for i := 0; i < rv.Len(); i++ { + prettyPrint(protect, sf, fmt.Sprintf("%d: ", i), ",\n", rv.Index(i).Interface(), hooks) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%u}" + suffix) + case reflect.Struct: + if rt.NumField() == 0 { + return + } + + if reflect.DeepEqual(reflect.Zero(rt).Interface(), rv.Interface()) { + return + } + + sf.Format("%s%T{%i\n", prefix, v) + for i := 0; i < rt.NumField(); i++ { + f := rv.Field(i) + if !f.CanInterface() { + continue + } + + prettyPrint(protect, sf, fmt.Sprintf("%s: ", rt.Field(i).Name), ",\n", f.Interface(), hooks) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%u}" + suffix) + case reflect.Ptr: + if rv.IsNil() { + return + } + + rvi := rv.Interface() + if _, ok := protect[rvi]; ok { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s&%T{ /* recursive/repetitive pointee not shown */ }"+suffix, prefix, rv.Elem().Interface()) + return + } + + if protect == nil { + protect = map[interface{}]struct{}{} + } + protect[rvi] = struct{}{} + prettyPrint(protect, sf, prefix+"&", suffix, rv.Elem().Interface(), hooks) + case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8: + if v := rv.Int(); v != 0 { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8: + if v := rv.Uint(); v != 0 { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.Float32, reflect.Float64: + if v := rv.Float(); v != 0 { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.Complex64, reflect.Complex128: + if v := rv.Complex(); v != 0 { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.Uintptr: + if v := rv.Uint(); v != 0 { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.UnsafePointer: + s := fmt.Sprintf("%p", rv.Interface()) + if s == "0x0" { + return + } + + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%s"+suffix, prefix, s) + case reflect.Bool: + if v := rv.Bool(); v { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, rv.Bool()) + } + case reflect.String: + s := rv.Interface().(string) + if s == "" { + return + } + + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%q"+suffix, prefix, s) + case reflect.Chan: + if reflect.Zero(rt).Interface() == rv.Interface() { + return + } + + c := rv.Cap() + s := "" + if c != 0 { + s = fmt.Sprintf("// capacity: %d", c) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%s %s%s"+suffix, prefix, rt.ChanDir(), rt.Elem().Name(), s) + case reflect.Func: + if rv.IsNil() { + return + } + + var in, out []string + for i := 0; i < rt.NumIn(); i++ { + x := reflect.Zero(rt.In(i)) + in = append(in, fmt.Sprintf("%T", x.Interface())) + } + if rt.IsVariadic() { + i := len(in) - 1 + in[i] = "..." + in[i][2:] + } + for i := 0; i < rt.NumOut(); i++ { + out = append(out, rt.Out(i).Name()) + } + s := "(" + strings.Join(in, ", ") + ")" + t := strings.Join(out, ", ") + if len(out) > 1 { + t = "(" + t + ")" + } + if t != "" { + t = " " + t + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%sfunc%s%s { ... }"+suffix, prefix, s, t) + case reflect.Map: + keys := rv.MapKeys() + if len(keys) == 0 { + return + } + + var buf bytes.Buffer + nf := IndentFormatter(&buf, "· ") + var skeys []string + for i, k := range keys { + prettyPrint(protect, nf, "", "", k.Interface(), hooks) + skeys = append(skeys, fmt.Sprintf("%s%10d", buf.Bytes(), i)) + buf.Reset() + } + sort.Strings(skeys) + sf.Format("%s%T{%i\n", prefix, v) + for _, k := range skeys { + si := strings.TrimSpace(k[len(k)-10:]) + k = k[:len(k)-10] + n, _ := strconv.ParseUint(si, 10, 64) + mv := rv.MapIndex(keys[n]) + prettyPrint(protect, sf, fmt.Sprintf("%s: ", k), ",\n", mv.Interface(), hooks) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%u}" + suffix) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/AUTHORS b/Godeps/_workspace/src/github.com/cznic/zappy/AUTHORS new file mode 100644 index 000000000..22c8397d5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/AUTHORS @@ -0,0 +1,12 @@ +# This file lists authors for copyright purposes. This file is distinct from +# the CONTRIBUTORS files. See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# +# The email address is not required for organizations. +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> +The Snappy-Go Authors diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/CONTRIBUTORS b/Godeps/_workspace/src/github.com/cznic/zappy/CONTRIBUTORS new file mode 100644 index 000000000..7693bdab7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/CONTRIBUTORS @@ -0,0 +1,10 @@ +# This file lists people who contributed code to this repository. The AUTHORS +# file lists the copyright holders; this file lists people. +# +# Names should be added to this file like so: +# Name +# +# Please keep the list sorted. + +Jan Mercl <0xjnml@gmail.com> +Mathieu Lonjaret diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/LICENSE b/Godeps/_workspace/src/github.com/cznic/zappy/LICENSE new file mode 100644 index 000000000..bc67059c5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The zappy Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the names of the authors nor the names of the +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/Makefile b/Godeps/_workspace/src/github.com/cznic/zappy/Makefile new file mode 100644 index 000000000..c6726d13e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/Makefile @@ -0,0 +1,30 @@ +# Copyright 2014 The zappy Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +all: editor + go tool vet -printfuncs Log:0,Logf:0 . + golint . + go install + make todo + +editor: + go fmt + go test -i + @#go test + ./purego.sh + +todo: + @grep -n ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alnum:]] *.go || true + @grep -n TODO *.go || true + @grep -n BUG *.go || true + @grep -n println *.go || true + +clean: + rm -f *~ cov cov.html + +gocov: + gocov test $(COV) | gocov-html > cov.html + +bench: + go test -run NONE -bench B diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/README.md b/Godeps/_workspace/src/github.com/cznic/zappy/README.md new file mode 100644 index 000000000..6fc6f68a7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/README.md @@ -0,0 +1,9 @@ +zappy +===== + +Package zappy implements the zappy block-based compression format. It aims for +a combination of good speed and reasonable compression. + +Installation: $ go get github.com/cznic/zappy + +Documentation: [godoc.org/github.com/cznic/zappy](http://godoc.org/github.com/cznic/zappy) diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/SNAPPY-GO-LICENSE b/Godeps/_workspace/src/github.com/cznic/zappy/SNAPPY-GO-LICENSE new file mode 100644 index 000000000..6050c10f4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/SNAPPY-GO-LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/all_test.go b/Godeps/_workspace/src/github.com/cznic/zappy/all_test.go new file mode 100644 index 000000000..ed79519d1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/all_test.go @@ -0,0 +1,379 @@ +// Copyright 2014 The zappy Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the SNAPPY-GO-LICENSE file. + +package zappy + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "math/rand" + "net/http" + "os" + "path" + "path/filepath" + "runtime" + "strings" + "testing" + + "code.google.com/p/snappy-go/snappy" +) + +var dbg = func(s string, va ...interface{}) { + _, fn, fl, _ := runtime.Caller(1) + fmt.Printf("%s:%d: ", path.Base(fn), fl) + fmt.Printf(s, va...) + fmt.Println() +} + +func use(...interface{}) {} + +var ( + download = flag.Bool("download", false, "If true, download any missing files before running benchmarks") + pureGo = flag.String("purego", "", "verify 'purego' build tag functionality for value `false` or `true`") +) + +func roundtrip(b, ebuf, dbuf []byte) error { + e, err := Encode(ebuf, b) + if err != nil { + return fmt.Errorf("encoding error: %v", err) + } + d, err := Decode(dbuf, e) + if err != nil { + return fmt.Errorf("decoding error: %v", err) + } + if !bytes.Equal(b, d) { + return fmt.Errorf("roundtrip mismatch:\n\twant %v\n\tgot %v", b, d) + } + return nil +} + +func TestEmpty(t *testing.T) { + if err := roundtrip(nil, nil, nil); err != nil { + t.Fatal(err) + } +} + +func TestSmallCopy(t *testing.T) { + for _, ebuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} { + for _, dbuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} { + for i := 0; i < 32; i++ { + s := "aaaa" + strings.Repeat("b", i) + "aaaabbbb" + if err := roundtrip([]byte(s), ebuf, dbuf); err != nil { + t.Fatalf("len(ebuf)=%d, len(dbuf)=%d, i=%d: %v", len(ebuf), len(dbuf), i, err) + } + } + } + } +} + +func TestSmallRand(t *testing.T) { + rand.Seed(27354294) + for n := 1; n < 20000; n += 23 { + b := make([]byte, n) + for i := range b { + b[i] = uint8(rand.Uint32()) + } + if err := roundtrip(b, nil, nil); err != nil { + t.Fatal(err) + } + } +} + +func TestSmallRegular(t *testing.T) { + for n := 1; n < 20000; n += 23 { + b := make([]byte, n) + for i := range b { + b[i] = uint8(i%10 + 'a') + } + if err := roundtrip(b, nil, nil); err != nil { + t.Fatal(n, err) + } + } +} + +func benchDecode(b *testing.B, src []byte) { + encoded, err := Encode(nil, src) + if err != nil { + b.Fatal(err) + } + // Bandwidth is in amount of uncompressed data. + b.SetBytes(int64(len(src))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Decode(src, encoded) + } +} + +func benchEncode(b *testing.B, src []byte) { + // Bandwidth is in amount of uncompressed data. + b.SetBytes(int64(len(src))) + dst := make([]byte, MaxEncodedLen(len(src))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Encode(dst, src) + } +} + +func readFile(b *testing.B, filename string) []byte { + src, err := ioutil.ReadFile(filename) + if err != nil { + b.Fatalf("failed reading %s: %s", filename, err) + } + if len(src) == 0 { + b.Fatalf("%s has zero length", filename) + } + return src +} + +func readFile2(t *testing.T, filename string) []byte { + src, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatalf("failed reading %s: %s", filename, err) + } + if len(src) == 0 { + t.Fatalf("%s has zero length", filename) + } + return src +} + +// expand returns a slice of length n containing repeated copies of src. +func expand(src []byte, n int) []byte { + dst := make([]byte, n) + for x := dst; len(x) > 0; { + i := copy(x, src) + x = x[i:] + } + return dst +} + +func benchWords(b *testing.B, n int, decode bool) { + // Note: the file is OS-language dependent so the resulting values are not + // directly comparable for non-US-English OS installations. + data := expand(readFile(b, "/usr/share/dict/words"), n) + if decode { + benchDecode(b, data) + } else { + benchEncode(b, data) + } +} + +func BenchmarkWordsDecode1e3(b *testing.B) { benchWords(b, 1e3, true) } +func BenchmarkWordsDecode1e4(b *testing.B) { benchWords(b, 1e4, true) } +func BenchmarkWordsDecode1e5(b *testing.B) { benchWords(b, 1e5, true) } +func BenchmarkWordsDecode1e6(b *testing.B) { benchWords(b, 1e6, true) } +func BenchmarkWordsEncode1e3(b *testing.B) { benchWords(b, 1e3, false) } +func BenchmarkWordsEncode1e4(b *testing.B) { benchWords(b, 1e4, false) } +func BenchmarkWordsEncode1e5(b *testing.B) { benchWords(b, 1e5, false) } +func BenchmarkWordsEncode1e6(b *testing.B) { benchWords(b, 1e6, false) } + +// testFiles' values are copied directly from +// https://code.google.com/p/snappy/source/browse/trunk/snappy_unittest.cc. +// The label field is unused in zappy. +var testFiles = []struct { + label string + filename string +}{ + {"html", "html"}, + {"urls", "urls.10K"}, + {"jpg", "house.jpg"}, + {"pdf", "mapreduce-osdi-1.pdf"}, + {"html4", "html_x_4"}, + {"cp", "cp.html"}, + {"c", "fields.c"}, + {"lsp", "grammar.lsp"}, + {"xls", "kennedy.xls"}, + {"txt1", "alice29.txt"}, + {"txt2", "asyoulik.txt"}, + {"txt3", "lcet10.txt"}, + {"txt4", "plrabn12.txt"}, + {"bin", "ptt5"}, + {"sum", "sum"}, + {"man", "xargs.1"}, + {"pb", "geo.protodata"}, + {"gaviota", "kppkn.gtb"}, +} + +// The test data files are present at this canonical URL. +const baseURL = "https://snappy.googlecode.com/svn/trunk/testdata/" + +func downloadTestdata(basename string) (errRet error) { + filename := filepath.Join("testdata", basename) + f, err := os.Create(filename) + if err != nil { + return fmt.Errorf("failed to create %s: %s", filename, err) + } + + defer f.Close() + defer func() { + if errRet != nil { + os.Remove(filename) + } + }() + resp, err := http.Get(baseURL + basename) + if err != nil { + return fmt.Errorf("failed to download %s: %s", baseURL+basename, err) + } + defer resp.Body.Close() + _, err = io.Copy(f, resp.Body) + if err != nil { + return fmt.Errorf("failed to write %s: %s", filename, err) + } + return nil +} + +func benchFile(b *testing.B, n int, decode bool) { + filename := filepath.Join("testdata", testFiles[n].filename) + if stat, err := os.Stat(filename); err != nil || stat.Size() == 0 { + if !*download { + b.Fatal("test data not found; skipping benchmark without the -download flag") + } + // Download the official snappy C++ implementation reference test data + // files for benchmarking. + if err := os.Mkdir("testdata", 0777); err != nil && !os.IsExist(err) { + b.Fatalf("failed to create testdata: %s", err) + } + for _, tf := range testFiles { + if err := downloadTestdata(tf.filename); err != nil { + b.Fatalf("failed to download testdata: %s", err) + } + } + } + data := readFile(b, filename) + if decode { + benchDecode(b, data) + } else { + benchEncode(b, data) + } +} + +// Naming convention is kept similar to what snappy's C++ implementation uses. +func Benchmark_UFlat0(b *testing.B) { benchFile(b, 0, true) } +func Benchmark_UFlat1(b *testing.B) { benchFile(b, 1, true) } +func Benchmark_UFlat2(b *testing.B) { benchFile(b, 2, true) } +func Benchmark_UFlat3(b *testing.B) { benchFile(b, 3, true) } +func Benchmark_UFlat4(b *testing.B) { benchFile(b, 4, true) } +func Benchmark_UFlat5(b *testing.B) { benchFile(b, 5, true) } +func Benchmark_UFlat6(b *testing.B) { benchFile(b, 6, true) } +func Benchmark_UFlat7(b *testing.B) { benchFile(b, 7, true) } +func Benchmark_UFlat8(b *testing.B) { benchFile(b, 8, true) } +func Benchmark_UFlat9(b *testing.B) { benchFile(b, 9, true) } +func Benchmark_UFlat10(b *testing.B) { benchFile(b, 10, true) } +func Benchmark_UFlat11(b *testing.B) { benchFile(b, 11, true) } +func Benchmark_UFlat12(b *testing.B) { benchFile(b, 12, true) } +func Benchmark_UFlat13(b *testing.B) { benchFile(b, 13, true) } +func Benchmark_UFlat14(b *testing.B) { benchFile(b, 14, true) } +func Benchmark_UFlat15(b *testing.B) { benchFile(b, 15, true) } +func Benchmark_UFlat16(b *testing.B) { benchFile(b, 16, true) } +func Benchmark_UFlat17(b *testing.B) { benchFile(b, 17, true) } +func Benchmark_ZFlat0(b *testing.B) { benchFile(b, 0, false) } +func Benchmark_ZFlat1(b *testing.B) { benchFile(b, 1, false) } +func Benchmark_ZFlat2(b *testing.B) { benchFile(b, 2, false) } +func Benchmark_ZFlat3(b *testing.B) { benchFile(b, 3, false) } +func Benchmark_ZFlat4(b *testing.B) { benchFile(b, 4, false) } +func Benchmark_ZFlat5(b *testing.B) { benchFile(b, 5, false) } +func Benchmark_ZFlat6(b *testing.B) { benchFile(b, 6, false) } +func Benchmark_ZFlat7(b *testing.B) { benchFile(b, 7, false) } +func Benchmark_ZFlat8(b *testing.B) { benchFile(b, 8, false) } +func Benchmark_ZFlat9(b *testing.B) { benchFile(b, 9, false) } +func Benchmark_ZFlat10(b *testing.B) { benchFile(b, 10, false) } +func Benchmark_ZFlat11(b *testing.B) { benchFile(b, 11, false) } +func Benchmark_ZFlat12(b *testing.B) { benchFile(b, 12, false) } +func Benchmark_ZFlat13(b *testing.B) { benchFile(b, 13, false) } +func Benchmark_ZFlat14(b *testing.B) { benchFile(b, 14, false) } +func Benchmark_ZFlat15(b *testing.B) { benchFile(b, 15, false) } +func Benchmark_ZFlat16(b *testing.B) { benchFile(b, 16, false) } +func Benchmark_ZFlat17(b *testing.B) { benchFile(b, 17, false) } + +func TestCmp(t *testing.T) { + var ts, tz, to int + for i := 0; i <= 17; i++ { + filename := filepath.Join("testdata", testFiles[i].filename) + if stat, err := os.Stat(filename); err != nil || stat.Size() == 0 { + if !*download { + t.Fatal("test data not found; skipping test without the -download flag") + } + // Download the official snappy C++ implementation reference test data + // files for benchmarking. + if err := os.Mkdir("testdata", 0777); err != nil && !os.IsExist(err) { + t.Fatalf("failed to create testdata: %s", err) + } + for _, tf := range testFiles { + if err := downloadTestdata(tf.filename); err != nil { + t.Fatalf("failed to download testdata: %s", err) + } + } + } + data := readFile2(t, filename) + orig := len(data) + to += orig + senc, err := snappy.Encode(nil, data) + if err != nil { + t.Fatal(err) + } + + ns := len(senc) + zenc, err := Encode(nil, data) + if err != nil { + t.Fatal(err) + } + + nz := len(zenc) + t.Logf("%35s: snappy %7d, zappy %7d, %.3f, orig %7d", filename, ns, nz, float64(nz)/float64(ns), orig) + ts += ns + tz += nz + } + t.Logf("%35s: snappy %7d, zappy %7d, %.3f, orig %7d", "TOTAL", ts, tz, float64(tz)/float64(ts), to) +} + +func TestBitIndex(t *testing.T) { + rng := rand.New(rand.NewSource(42)) + for n := 16; n <= 1<<16; n <<= 1 { + data := make([]byte, n) + for i := 0; i < n/1000+1; i++ { + data[rng.Int()%n] = 1 + } + + senc, err := snappy.Encode(nil, data) + if err != nil { + t.Fatal(err) + } + + ns := len(senc) + zenc, err := Encode(nil, data) + if err != nil { + t.Fatal(err) + } + + nz := len(zenc) + t.Logf("Sparse bit index %7d B: snappy %7d, zappy %7d, %.3f", n, ns, nz, float64(nz)/float64(ns)) + } +} + +func TestPureGo(t *testing.T) { + var purego bool + switch s := *pureGo; s { + case "false": + // nop + case "true": + purego = true + default: + t.Logf("Not performed: %q", s) + return + } + + if g, e := puregoDecode(), purego; g != e { + t.Fatal("Decode", g, e) + } + + if g, e := puregoEncode(), purego; g != e { + t.Fatal("Encode", g, e) + } +} diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/decode.go b/Godeps/_workspace/src/github.com/cznic/zappy/decode.go new file mode 100644 index 000000000..867c8deea --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/decode.go @@ -0,0 +1,38 @@ +// Copyright 2014 The zappy Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the SNAPPY-GO-LICENSE file. + +package zappy + +import ( + "encoding/binary" + "errors" +) + +// ErrCorrupt reports that the input is invalid. +var ErrCorrupt = errors.New("zappy: corrupt input") + +// DecodedLen returns the length of the decoded block. +func DecodedLen(src []byte) (int, error) { + v, _, err := decodedLen(src) + return v, err +} + +// decodedLen returns the length of the decoded block and the number of bytes +// that the length header occupied. +func decodedLen(src []byte) (blockLen, headerLen int, err error) { + v, n := binary.Uvarint(src) + if n == 0 { + return 0, 0, ErrCorrupt + } + + if uint64(int(v)) != v { + return 0, 0, errors.New("zappy: decoded block is too large") + } + + return int(v), n, nil +} diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/decode_cgo.go b/Godeps/_workspace/src/github.com/cznic/zappy/decode_cgo.go new file mode 100644 index 000000000..993c99242 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/decode_cgo.go @@ -0,0 +1,121 @@ +// Copyright 2014 The zappy Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the SNAPPY-GO-LICENSE file. + +// +build cgo,!purego + +package zappy + +/* + +#include +#include + +// supports only uint32 encoded values +int uvarint(unsigned int* n, uint8_t* src, int len) { + int r = 0; + unsigned int v = 0; + unsigned int s = 0; + while ((len-- != 0) && (++r <= 5)) { + uint8_t b = *src++; + v = v | ((b&0x7f)<>1; + if ((u&1) != 0) + x = ~x; + *n = x; + return i; +} + +int decode(int s, int len_src, uint8_t* src, int len_dst, uint8_t* dst) { + int d = 0; + int length; + while (s < len_src) { + int n, i = varint(&n, src+s, len_src-s); + if (i <= 0) { + return -1; + } + + s += i; + if (n >= 0) { + length = n+1; + if ((length > len_dst-d) || (length > len_src-s)) + return -1; + + memcpy(dst+d, src+s, length); + d += length; + s += length; + continue; + } + + + length = -n; + int offset; + i = uvarint((unsigned int*)(&offset), src+s, len_src-s); + if (i <= 0) + return -1; + + s += i; + if (s > len_src) + return -1; + + int end = d+length; + if ((offset > d) || (end > len_dst)) + return -1; + + for( ; d < end; d++) + *(dst+d) = *(dst+d-offset); + } + return d; +} + +*/ +import "C" + +func puregoDecode() bool { return false } + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of buf if buf was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// It is valid to pass a nil buf. +func Decode(buf, src []byte) ([]byte, error) { + dLen, s, err := decodedLen(src) + if err != nil { + return nil, err + } + + if dLen == 0 { + if len(src) == 1 { + return nil, nil + } + + return nil, ErrCorrupt + } + + if len(buf) < dLen { + buf = make([]byte, dLen) + } + + d := int(C.decode(C.int(s), C.int(len(src)), (*C.uint8_t)(&src[0]), C.int(len(buf)), (*C.uint8_t)(&buf[0]))) + if d != dLen { + return nil, ErrCorrupt + } + + return buf[:d], nil +} diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/decode_nocgo.go b/Godeps/_workspace/src/github.com/cznic/zappy/decode_nocgo.go new file mode 100644 index 000000000..75b782f8f --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/decode_nocgo.go @@ -0,0 +1,89 @@ +// Copyright 2014 The zappy Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the SNAPPY-GO-LICENSE file. + +// +build !cgo purego + +package zappy + +import ( + "encoding/binary" +) + +func puregoDecode() bool { return true } + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of buf if buf was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// It is valid to pass a nil buf. +func Decode(buf, src []byte) ([]byte, error) { + dLen, s, err := decodedLen(src) + if err != nil { + return nil, err + } + + if dLen == 0 { + if len(src) == 1 { + return nil, nil + } + + return nil, ErrCorrupt + } + + if len(buf) < dLen { + buf = make([]byte, dLen) + } + + var d, offset, length int + for s < len(src) { + n, i := binary.Varint(src[s:]) + if i <= 0 { + return nil, ErrCorrupt + } + + s += i + if n >= 0 { + length = int(n + 1) + if length > len(buf)-d || length > len(src)-s { + return nil, ErrCorrupt + } + + copy(buf[d:], src[s:s+length]) + d += length + s += length + continue + } + + length = int(-n) + off64, i := binary.Uvarint(src[s:]) + if i <= 0 { + return nil, ErrCorrupt + } + + offset = int(off64) + s += i + if s > len(src) { + return nil, ErrCorrupt + } + + end := d + length + if offset > d || end > len(buf) { + return nil, ErrCorrupt + } + + for s, v := range buf[d-offset : end-offset] { + buf[d+s] = v + } + d = end + + } + if d != dLen { + return nil, ErrCorrupt + } + + return buf[:d], nil +} diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/encode.go b/Godeps/_workspace/src/github.com/cznic/zappy/encode.go new file mode 100644 index 000000000..27ceba03b --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/encode.go @@ -0,0 +1,37 @@ +// Copyright 2014 The zappy Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the SNAPPY-GO-LICENSE file. + +package zappy + +import ( + "encoding/binary" +) + +// We limit how far copy back-references can go, the same as the snappy C++ +// code. +const maxOffset = 1 << 20 + +// emitCopy writes a copy chunk and returns the number of bytes written. +func emitCopy(dst []byte, offset, length int) (n int) { + n = binary.PutVarint(dst, int64(-length)) + n += binary.PutUvarint(dst[n:], uint64(offset)) + return +} + +// emitLiteral writes a literal chunk and returns the number of bytes written. +func emitLiteral(dst, lit []byte) (n int) { + n = binary.PutVarint(dst, int64(len(lit)-1)) + n += copy(dst[n:], lit) + return +} + +// MaxEncodedLen returns the maximum length of a zappy block, given its +// uncompressed length. +func MaxEncodedLen(srcLen int) int { + return 10 + srcLen +} diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/encode_cgo.go b/Godeps/_workspace/src/github.com/cznic/zappy/encode_cgo.go new file mode 100644 index 000000000..6c343f58e --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/encode_cgo.go @@ -0,0 +1,140 @@ +// Copyright 2014 The zappy Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the SNAPPY-GO-LICENSE file. + +// +build cgo,!purego + +package zappy + +/* + +#include +#include + +#define MAXOFFSET 1<<20 + +int putUvarint(uint8_t* buf, unsigned int x) { + int i = 1; + for (; x >= 0x80; i++) { + *buf++ = x|0x80; + x >>= 7; + } + *buf = x; + return i; +} + +int putVarint(uint8_t* buf, int x) { + unsigned int ux = x << 1; + if (x < 0) + ux = ~ux; + return putUvarint(buf, ux); +} + +int emitLiteral(uint8_t* dst, uint8_t* lit, int len_lit) { + int n = putVarint(dst, len_lit-1); + memcpy(dst+n, lit, len_lit); + return n+len_lit; +} + +int emitCopy(uint8_t* dst, int off, int len) { + int n = putVarint(dst, -len); + return n+putUvarint(dst+n, (unsigned int)off); +} + +int encode(int d, uint8_t* dst, uint8_t* src, int len_src) { + int table[1<<12]; + int s = 0; + int t = 0; + int lit = 0; + int lim = 0; + memset(table, 0, sizeof(table)); + for (lim = len_src-3; s < lim; ) { + // Update the hash table. + uint32_t b0 = src[s]; + uint32_t b1 = src[s+1]; + uint32_t b2 = src[s+2]; + uint32_t b3 = src[s+3]; + uint32_t h = b0 | (b1<<8) | (b2<<16) | (b3<<24); + uint32_t i; +more: + i = (h*0x1e35a7bd)>>20; + t = table[i]; + table[i] = s; + // If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte. + if ((t == 0) || (s-t >= MAXOFFSET) || (b0 != src[t]) || (b1 != src[t+1]) || (b2 != src[t+2]) || (b3 != src[t+3])) { + s++; + if (s >= lim) + break; + + b0 = b1; + b1 = b2; + b2 = b3; + b3 = src[s+3]; + h = (h>>8) | ((b3)<<24); + goto more; + } + + // Otherwise, we have a match. First, emit any pending literal bytes. + if (lit != s) { + d += emitLiteral(dst+d, src+lit, s-lit); + } + // Extend the match to be as long as possible. + int s0 = s; + s += 4; + t += 4; + while ((s < len_src) && (src[s] == src[t])) { + s++; + t++; + } + d += emitCopy(dst+d, s-t, s-s0); + lit = s; + } + // Emit any final pending literal bytes and return. + if (lit != len_src) { + d += emitLiteral(dst+d, src+lit, len_src-lit); + } + return d; +} + +*/ +import "C" + +import ( + "encoding/binary" + "fmt" + "math" +) + +func puregoEncode() bool { return false } + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of buf if buf was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// It is valid to pass a nil buf. +func Encode(buf, src []byte) ([]byte, error) { + if n := MaxEncodedLen(len(src)); len(buf) < n { + buf = make([]byte, n) + } + + if len(src) > math.MaxInt32 { + return nil, fmt.Errorf("zappy.Encode: too long data: %d bytes", len(src)) + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(buf, uint64(len(src))) + + // Return early if src is short. + if len(src) <= 4 { + if len(src) != 0 { + d += emitLiteral(buf[d:], src) + } + return buf[:d], nil + } + + d = int(C.encode(C.int(d), (*C.uint8_t)(&buf[0]), (*C.uint8_t)(&src[0]), C.int(len(src)))) + return buf[:d], nil +} diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/encode_nocgo.go b/Godeps/_workspace/src/github.com/cznic/zappy/encode_nocgo.go new file mode 100644 index 000000000..1d1df4439 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/encode_nocgo.go @@ -0,0 +1,92 @@ +// Copyright 2014 The zappy Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the SNAPPY-GO-LICENSE file. + +// +build !cgo purego + +package zappy + +import ( + "encoding/binary" + "fmt" + "math" +) + +func puregoEncode() bool { return true } + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of buf if buf was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// It is valid to pass a nil buf. +func Encode(buf, src []byte) ([]byte, error) { + if n := MaxEncodedLen(len(src)); len(buf) < n { + buf = make([]byte, n) + } + + if len(src) > math.MaxInt32 { + return nil, fmt.Errorf("zappy.Encode: too long data: %d bytes", len(src)) + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(buf, uint64(len(src))) + + // Return early if src is short. + if len(src) <= 4 { + if len(src) != 0 { + d += emitLiteral(buf[d:], src) + } + return buf[:d], nil + } + + // Iterate over the source bytes. + var ( + table [1 << 12]int // Hash table + s int // The iterator position. + t int // The last position with the same hash as s. + lit int // The start position of any pending literal bytes. + ) + for lim := len(src) - 3; s < lim; { + // Update the hash table. + b0, b1, b2, b3 := src[s], src[s+1], src[s+2], src[s+3] + h := uint32(b0) | uint32(b1)<<8 | uint32(b2)<<16 | uint32(b3)<<24 + more: + p := &table[(h*0x1e35a7bd)>>20] + t, *p = *p, s + // If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte. + if t == 0 || s-t >= maxOffset || b0 != src[t] || b1 != src[t+1] || b2 != src[t+2] || b3 != src[t+3] { + s++ + if s >= lim { + break + } + + b0, b1, b2, b3 = b1, b2, b3, src[s+3] + h = h>>8 | uint32(b3)<<24 + goto more + } + + // Otherwise, we have a match. First, emit any pending literal bytes. + if lit != s { + d += emitLiteral(buf[d:], src[lit:s]) + } + // Extend the match to be as long as possible. + s0 := s + s, t = s+4, t+4 + for s < len(src) && src[s] == src[t] { + s++ + t++ + } + // Emit the copied bytes. + d += emitCopy(buf[d:], s-t, s-s0) + lit = s + } + + // Emit any final pending literal bytes and return. + if lit != len(src) { + d += emitLiteral(buf[d:], src[lit:]) + } + return buf[:d], nil +} diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/purego.sh b/Godeps/_workspace/src/github.com/cznic/zappy/purego.sh new file mode 100644 index 000000000..e2fec14e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/purego.sh @@ -0,0 +1,11 @@ +# Copyright 2014 The zappy Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +set -e +set -v + +CGO_ENABLED=0 go test -purego true +CGO_ENABLED=0 go test -purego true -tags purego +CGO_ENABLED=1 go test -purego false +CGO_ENABLED=1 go test -purego true -tags purego diff --git a/Godeps/_workspace/src/github.com/cznic/zappy/zappy.go b/Godeps/_workspace/src/github.com/cznic/zappy/zappy.go new file mode 100644 index 000000000..760df34a1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/cznic/zappy/zappy.go @@ -0,0 +1,241 @@ +// Copyright 2014 The zappy Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the SNAPPY-GO-LICENSE file. + +/* +Package zappy implements the zappy block-based compression format. It aims for +a combination of good speed and reasonable compression. + +Zappy is a format incompatible, API compatible fork of snappy-go[1]. The C++ +snappy implementation is at [2]. + +Reasons for the fork + +The snappy compression is pretty good. Yet it has one problem built into its +format definition[3] - the maximum length of a copy "instruction" is 64 bytes. +For some specific usage patterns with long runs of repeated data, it turns out +the compression is suboptimal. For example a 1:1000 "sparseness" 64kB bit index +with only few set bits is compressed to about 3kB (about 1000 of 64B copy, 3 +byte "instructions"). + +Format description + +Zappy uses much less complicated format than snappy. Each encoded block begins +with the uvarint-encoded[4] length of the decoded data, followed by a sequence +of chunks. Chunks begin and end on byte boundaries. The chunk starts with a +varint encoded number N: + + N >= 0: N+1 literal bytes follow. + + N < 0: copy -N bytes, starting at offset M (in the following uvarint). + +Performance issues + +Compression rate is roughly the same as of snappy for the reference data set: + + testdata/html: snappy 23320, zappy 22943, 0.984, orig 102400 + testdata/urls.10K: snappy 334437, zappy 355163, 1.062, orig 702087 + testdata/house.jpg: snappy 126711, zappy 126694, 1.000, orig 126958 + testdata/mapreduce-osdi-1.pdf: snappy 77227, zappy 77646, 1.005, orig 94330 + testdata/html_x_4: snappy 92350, zappy 22956, 0.249, orig 409600 + testdata/cp.html: snappy 11938, zappy 12961, 1.086, orig 24603 + testdata/fields.c: snappy 4825, zappy 5395, 1.118, orig 11150 + testdata/grammar.lsp: snappy 1814, zappy 1933, 1.066, orig 3721 + testdata/kennedy.xls: snappy 423518, zappy 440597, 1.040, orig 1029744 + testdata/alice29.txt: snappy 89550, zappy 104016, 1.162, orig 152089 + testdata/asyoulik.txt: snappy 79583, zappy 91345, 1.148, orig 125179 + testdata/lcet10.txt: snappy 238761, zappy 275488, 1.154, orig 426754 + testdata/plrabn12.txt: snappy 324567, zappy 376885, 1.161, orig 481861 + testdata/ptt5: snappy 96350, zappy 91465, 0.949, orig 513216 + testdata/sum: snappy 18927, zappy 20015, 1.057, orig 38240 + testdata/xargs.1: snappy 2532, zappy 2793, 1.103, orig 4227 + testdata/geo.protodata: snappy 23362, zappy 20759, 0.889, orig 118588 + testdata/kppkn.gtb: snappy 73962, zappy 87200, 1.179, orig 184320 + TOTAL: snappy 2043734, zappy 2136254, 1.045, orig 4549067 + +Zappy has better RLE handling (1/1000+1 non zero bytes in each index): + + Sparse bit index 16 B: snappy 9, zappy 9, 1.000 + Sparse bit index 32 B: snappy 10, zappy 10, 1.000 + Sparse bit index 64 B: snappy 11, zappy 10, 0.909 + Sparse bit index 128 B: snappy 16, zappy 14, 0.875 + Sparse bit index 256 B: snappy 22, zappy 14, 0.636 + Sparse bit index 512 B: snappy 36, zappy 16, 0.444 + Sparse bit index 1024 B: snappy 57, zappy 18, 0.316 + Sparse bit index 2048 B: snappy 111, zappy 32, 0.288 + Sparse bit index 4096 B: snappy 210, zappy 31, 0.148 + Sparse bit index 8192 B: snappy 419, zappy 75, 0.179 + Sparse bit index 16384 B: snappy 821, zappy 138, 0.168 + Sparse bit index 32768 B: snappy 1627, zappy 232, 0.143 + Sparse bit index 65536 B: snappy 3243, zappy 451, 0.139 + +When compiled with CGO_ENABLED=1, zappy is now faster than snappy-go. +Old=snappy-go, new=zappy: + + benchmark old MB/s new MB/s speedup + BenchmarkWordsDecode1e3 148.98 189.04 1.27x + BenchmarkWordsDecode1e4 150.29 182.51 1.21x + BenchmarkWordsDecode1e5 145.79 182.95 1.25x + BenchmarkWordsDecode1e6 167.43 187.69 1.12x + BenchmarkWordsEncode1e3 47.11 145.69 3.09x + BenchmarkWordsEncode1e4 81.47 136.50 1.68x + BenchmarkWordsEncode1e5 78.86 127.93 1.62x + BenchmarkWordsEncode1e6 96.81 142.95 1.48x + Benchmark_UFlat0 316.87 463.19 1.46x + Benchmark_UFlat1 231.56 350.32 1.51x + Benchmark_UFlat2 3656.68 8258.39 2.26x + Benchmark_UFlat3 892.56 1270.09 1.42x + Benchmark_UFlat4 315.84 959.08 3.04x + Benchmark_UFlat5 211.70 301.55 1.42x + Benchmark_UFlat6 211.59 258.29 1.22x + Benchmark_UFlat7 209.80 272.21 1.30x + Benchmark_UFlat8 254.59 301.70 1.19x + Benchmark_UFlat9 163.39 192.66 1.18x + Benchmark_UFlat10 155.46 189.70 1.22x + Benchmark_UFlat11 170.11 198.95 1.17x + Benchmark_UFlat12 148.32 178.78 1.21x + Benchmark_UFlat13 359.25 579.99 1.61x + Benchmark_UFlat14 197.27 291.33 1.48x + Benchmark_UFlat15 185.75 248.07 1.34x + Benchmark_UFlat16 362.74 582.66 1.61x + Benchmark_UFlat17 222.95 240.01 1.08x + Benchmark_ZFlat0 188.66 311.89 1.65x + Benchmark_ZFlat1 101.46 201.34 1.98x + Benchmark_ZFlat2 93.62 244.50 2.61x + Benchmark_ZFlat3 102.79 243.34 2.37x + Benchmark_ZFlat4 191.64 625.32 3.26x + Benchmark_ZFlat5 103.09 169.39 1.64x + Benchmark_ZFlat6 110.35 182.57 1.65x + Benchmark_ZFlat7 89.56 190.53 2.13x + Benchmark_ZFlat8 154.05 235.68 1.53x + Benchmark_ZFlat9 87.58 133.51 1.52x + Benchmark_ZFlat10 82.08 127.51 1.55x + Benchmark_ZFlat11 91.36 138.91 1.52x + Benchmark_ZFlat12 79.24 123.02 1.55x + Benchmark_ZFlat13 217.04 374.26 1.72x + Benchmark_ZFlat14 100.33 168.03 1.67x + Benchmark_ZFlat15 80.79 160.46 1.99x + Benchmark_ZFlat16 213.32 375.79 1.76x + Benchmark_ZFlat17 135.37 197.13 1.46x + +The package builds with CGO_ENABLED=0 as well, but the performance is worse. + + + $ CGO_ENABLED=0 go test -test.run=NONE -test.bench=. > old.benchcmp + $ CGO_ENABLED=1 go test -test.run=NONE -test.bench=. > new.benchcmp + $ benchcmp old.benchcmp new.benchcmp + benchmark old ns/op new ns/op delta + BenchmarkWordsDecode1e3 9735 5288 -45.68% + BenchmarkWordsDecode1e4 100229 55369 -44.76% + BenchmarkWordsDecode1e5 1037611 546420 -47.34% + BenchmarkWordsDecode1e6 9559352 5335307 -44.19% + BenchmarkWordsEncode1e3 16206 6629 -59.10% + BenchmarkWordsEncode1e4 140283 73161 -47.85% + BenchmarkWordsEncode1e5 1476657 781756 -47.06% + BenchmarkWordsEncode1e6 12702229 6997656 -44.91% + Benchmark_UFlat0 397307 221198 -44.33% + Benchmark_UFlat1 3890483 2008341 -48.38% + Benchmark_UFlat2 35810 15398 -57.00% + Benchmark_UFlat3 140850 74194 -47.32% + Benchmark_UFlat4 814575 426783 -47.61% + Benchmark_UFlat5 156995 81473 -48.10% + Benchmark_UFlat6 77645 43161 -44.41% + Benchmark_UFlat7 25415 13579 -46.57% + Benchmark_UFlat8 6372440 3412916 -46.44% + Benchmark_UFlat9 1453679 789956 -45.66% + Benchmark_UFlat10 1243146 660747 -46.85% + Benchmark_UFlat11 3903493 2146334 -45.02% + Benchmark_UFlat12 5106250 2696144 -47.20% + Benchmark_UFlat13 1641394 884969 -46.08% + Benchmark_UFlat14 262206 131174 -49.97% + Benchmark_UFlat15 32325 17047 -47.26% + Benchmark_UFlat16 366991 204877 -44.17% + Benchmark_UFlat17 1343988 770907 -42.64% + Benchmark_ZFlat0 579954 329812 -43.13% + Benchmark_ZFlat1 6564692 3504867 -46.61% + Benchmark_ZFlat2 902029 513700 -43.05% + Benchmark_ZFlat3 678722 384312 -43.38% + Benchmark_ZFlat4 1197389 654361 -45.35% + Benchmark_ZFlat5 262677 144939 -44.82% + Benchmark_ZFlat6 111249 60876 -45.28% + Benchmark_ZFlat7 39024 19420 -50.24% + Benchmark_ZFlat8 8046106 4387928 -45.47% + Benchmark_ZFlat9 2043167 1143139 -44.05% + Benchmark_ZFlat10 1781604 980528 -44.96% + Benchmark_ZFlat11 5478647 3078585 -43.81% + Benchmark_ZFlat12 7245995 3929863 -45.77% + Benchmark_ZFlat13 2432529 1371606 -43.61% + Benchmark_ZFlat14 420315 227494 -45.88% + Benchmark_ZFlat15 52378 26564 -49.28% + Benchmark_ZFlat16 567047 316196 -44.24% + Benchmark_ZFlat17 1630820 937310 -42.53% + + benchmark old MB/s new MB/s speedup + BenchmarkWordsDecode1e3 102.71 189.08 1.84x + BenchmarkWordsDecode1e4 99.77 180.60 1.81x + BenchmarkWordsDecode1e5 96.38 183.01 1.90x + BenchmarkWordsDecode1e6 104.61 187.43 1.79x + BenchmarkWordsEncode1e3 61.70 150.85 2.44x + BenchmarkWordsEncode1e4 71.28 136.68 1.92x + BenchmarkWordsEncode1e5 67.72 127.92 1.89x + BenchmarkWordsEncode1e6 78.73 142.90 1.82x + Benchmark_UFlat0 257.73 462.93 1.80x + Benchmark_UFlat1 180.46 349.59 1.94x + Benchmark_UFlat2 3545.30 8244.61 2.33x + Benchmark_UFlat3 669.72 1271.39 1.90x + Benchmark_UFlat4 502.84 959.74 1.91x + Benchmark_UFlat5 156.71 301.98 1.93x + Benchmark_UFlat6 143.60 258.33 1.80x + Benchmark_UFlat7 146.41 274.01 1.87x + Benchmark_UFlat8 161.59 301.72 1.87x + Benchmark_UFlat9 104.62 192.53 1.84x + Benchmark_UFlat10 100.70 189.45 1.88x + Benchmark_UFlat11 109.33 198.83 1.82x + Benchmark_UFlat12 94.37 178.72 1.89x + Benchmark_UFlat13 312.67 579.93 1.85x + Benchmark_UFlat14 145.84 291.52 2.00x + Benchmark_UFlat15 130.77 247.95 1.90x + Benchmark_UFlat16 323.14 578.82 1.79x + Benchmark_UFlat17 137.14 239.09 1.74x + Benchmark_ZFlat0 176.57 310.48 1.76x + Benchmark_ZFlat1 106.95 200.32 1.87x + Benchmark_ZFlat2 140.75 247.14 1.76x + Benchmark_ZFlat3 138.98 245.45 1.77x + Benchmark_ZFlat4 342.08 625.95 1.83x + Benchmark_ZFlat5 93.66 169.75 1.81x + Benchmark_ZFlat6 100.23 183.16 1.83x + Benchmark_ZFlat7 95.35 191.60 2.01x + Benchmark_ZFlat8 127.98 234.68 1.83x + Benchmark_ZFlat9 74.44 133.04 1.79x + Benchmark_ZFlat10 70.26 127.66 1.82x + Benchmark_ZFlat11 77.89 138.62 1.78x + Benchmark_ZFlat12 66.50 122.62 1.84x + Benchmark_ZFlat13 210.98 374.17 1.77x + Benchmark_ZFlat14 90.98 168.09 1.85x + Benchmark_ZFlat15 80.70 159.12 1.97x + Benchmark_ZFlat16 209.13 375.04 1.79x + Benchmark_ZFlat17 113.02 196.65 1.74x + $ + +Build tags + +If a constraint 'purego' appears in the build constraints [5] then a pure Go +version is built regardless of the $CGO_ENABLED value. + + $ touch zappy.go ; go install -tags purego github.com/cznic/zappy # for example + +Information sources + +... referenced from the above documentation. + + [1]: http://code.google.com/p/snappy-go/ + [2]: http://code.google.com/p/snappy/ + [3]: http://code.google.com/p/snappy/source/browse/trunk/format_description.txt + [4]: http://golang.org/pkg/encoding/binary/ + [5]: http://golang.org/pkg/go/build/#hdr-Build_Constraints +*/ +package zappy diff --git a/Godeps/_workspace/src/github.com/golang/groupcache/lru/lru.go b/Godeps/_workspace/src/github.com/golang/groupcache/lru/lru.go new file mode 100644 index 000000000..cdfe2991f --- /dev/null +++ b/Godeps/_workspace/src/github.com/golang/groupcache/lru/lru.go @@ -0,0 +1,121 @@ +/* +Copyright 2013 Google Inc. + +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 lru implements an LRU cache. +package lru + +import "container/list" + +// Cache is an LRU cache. It is not safe for concurrent access. +type Cache struct { + // MaxEntries is the maximum number of cache entries before + // an item is evicted. Zero means no limit. + MaxEntries int + + // OnEvicted optionally specificies a callback function to be + // executed when an entry is purged from the cache. + OnEvicted func(key Key, value interface{}) + + ll *list.List + cache map[interface{}]*list.Element +} + +// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators +type Key interface{} + +type entry struct { + key Key + value interface{} +} + +// New creates a new Cache. +// If maxEntries is zero, the cache has no limit and it's assumed +// that eviction is done by the caller. +func New(maxEntries int) *Cache { + return &Cache{ + MaxEntries: maxEntries, + ll: list.New(), + cache: make(map[interface{}]*list.Element), + } +} + +// Add adds a value to the cache. +func (c *Cache) Add(key Key, value interface{}) { + if c.cache == nil { + c.cache = make(map[interface{}]*list.Element) + c.ll = list.New() + } + if ee, ok := c.cache[key]; ok { + c.ll.MoveToFront(ee) + ee.Value.(*entry).value = value + return + } + ele := c.ll.PushFront(&entry{key, value}) + c.cache[key] = ele + if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { + c.RemoveOldest() + } +} + +// Get looks up a key's value from the cache. +func (c *Cache) Get(key Key) (value interface{}, ok bool) { + if c.cache == nil { + return + } + if ele, hit := c.cache[key]; hit { + c.ll.MoveToFront(ele) + return ele.Value.(*entry).value, true + } + return +} + +// Remove removes the provided key from the cache. +func (c *Cache) Remove(key Key) { + if c.cache == nil { + return + } + if ele, hit := c.cache[key]; hit { + c.removeElement(ele) + } +} + +// RemoveOldest removes the oldest item from the cache. +func (c *Cache) RemoveOldest() { + if c.cache == nil { + return + } + ele := c.ll.Back() + if ele != nil { + c.removeElement(ele) + } +} + +func (c *Cache) removeElement(e *list.Element) { + c.ll.Remove(e) + kv := e.Value.(*entry) + delete(c.cache, kv.key) + if c.OnEvicted != nil { + c.OnEvicted(kv.key, kv.value) + } +} + +// Len returns the number of items in the cache. +func (c *Cache) Len() int { + if c.cache == nil { + return 0 + } + return c.ll.Len() +} diff --git a/Godeps/_workspace/src/github.com/golang/groupcache/lru/lru_test.go b/Godeps/_workspace/src/github.com/golang/groupcache/lru/lru_test.go new file mode 100644 index 000000000..98a2656e8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/golang/groupcache/lru/lru_test.go @@ -0,0 +1,73 @@ +/* +Copyright 2013 Google Inc. + +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 lru + +import ( + "testing" +) + +type simpleStruct struct { + int + string +} + +type complexStruct struct { + int + simpleStruct +} + +var getTests = []struct { + name string + keyToAdd interface{} + keyToGet interface{} + expectedOk bool +}{ + {"string_hit", "myKey", "myKey", true}, + {"string_miss", "myKey", "nonsense", false}, + {"simple_struct_hit", simpleStruct{1, "two"}, simpleStruct{1, "two"}, true}, + {"simeple_struct_miss", simpleStruct{1, "two"}, simpleStruct{0, "noway"}, false}, + {"complex_struct_hit", complexStruct{1, simpleStruct{2, "three"}}, + complexStruct{1, simpleStruct{2, "three"}}, true}, +} + +func TestGet(t *testing.T) { + for _, tt := range getTests { + lru := New(0) + lru.Add(tt.keyToAdd, 1234) + val, ok := lru.Get(tt.keyToGet) + if ok != tt.expectedOk { + t.Fatalf("%s: cache hit = %v; want %v", tt.name, ok, !ok) + } else if ok && val != 1234 { + t.Fatalf("%s expected get to return 1234 but got %v", tt.name, val) + } + } +} + +func TestRemove(t *testing.T) { + lru := New(0) + lru.Add("myKey", 1234) + if val, ok := lru.Get("myKey"); !ok { + t.Fatal("TestRemove returned no match") + } else if val != 1234 { + t.Fatalf("TestRemove failed. Expected %d, got %v", 1234, val) + } + + lru.Remove("myKey") + if _, ok := lru.Get("myKey"); ok { + t.Fatal("TestRemove returned a removed entry") + } +} diff --git a/Godeps/_workspace/src/github.com/juju/ratelimit/LICENSE b/Godeps/_workspace/src/github.com/juju/ratelimit/LICENSE new file mode 100644 index 000000000..ade9307b3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/juju/ratelimit/LICENSE @@ -0,0 +1,191 @@ +All files in this repository are licensed as follows. If you contribute +to this repository, it is assumed that you license your contribution +under the same license unless you state otherwise. + +All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file. + +This software is licensed under the LGPLv3, included below. + +As a special exception to the GNU Lesser General Public License version 3 +("LGPL3"), the copyright holders of this Library give you permission to +convey to a third party a Combined Work that links statically or dynamically +to this Library without providing any Minimal Corresponding Source or +Minimal Application Code as set out in 4d or providing the installation +information set out in section 4e, provided that you comply with the other +provisions of LGPL3 and provided that you meet, for the Application the +terms and conditions of the license(s) which apply to the Application. + +Except as stated in this special exception, the provisions of LGPL3 will +continue to comply in full to this Library. If you modify this Library, you +may apply this exception to your version of this Library, but you are not +obliged to do so. If you do not wish to do so, delete this exception +statement from your version. This exception does not (and cannot) modify any +license terms which apply to the Application, with which you must still +comply. + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/Godeps/_workspace/src/github.com/juju/ratelimit/README.md b/Godeps/_workspace/src/github.com/juju/ratelimit/README.md new file mode 100644 index 000000000..a0fdfe2b1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/juju/ratelimit/README.md @@ -0,0 +1,117 @@ +# ratelimit +-- + import "github.com/juju/ratelimit" + +The ratelimit package provides an efficient token bucket implementation. See +http://en.wikipedia.org/wiki/Token_bucket. + +## Usage + +#### func Reader + +```go +func Reader(r io.Reader, bucket *Bucket) io.Reader +``` +Reader returns a reader that is rate limited by the given token bucket. Each +token in the bucket represents one byte. + +#### func Writer + +```go +func Writer(w io.Writer, bucket *Bucket) io.Writer +``` +Writer returns a writer that is rate limited by the given token bucket. Each +token in the bucket represents one byte. + +#### type Bucket + +```go +type Bucket struct { +} +``` + +Bucket represents a token bucket that fills at a predetermined rate. Methods on +Bucket may be called concurrently. + +#### func NewBucket + +```go +func NewBucket(fillInterval time.Duration, capacity int64) *Bucket +``` +NewBucket returns a new token bucket that fills at the rate of one token every +fillInterval, up to the given maximum capacity. Both arguments must be positive. +The bucket is initially full. + +#### func NewBucketWithQuantum + +```go +func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket +``` +NewBucketWithQuantum is similar to NewBucket, but allows the specification of +the quantum size - quantum tokens are added every fillInterval. + +#### func NewBucketWithRate + +```go +func NewBucketWithRate(rate float64, capacity int64) *Bucket +``` +NewBucketWithRate returns a token bucket that fills the bucket at the rate of +rate tokens per second up to the given maximum capacity. Because of limited +clock resolution, at high rates, the actual rate may be up to 1% different from +the specified rate. + +#### func (*Bucket) Rate + +```go +func (tb *Bucket) Rate() float64 +``` +Rate returns the fill rate of the bucket, in tokens per second. + +#### func (*Bucket) Take + +```go +func (tb *Bucket) Take(count int64) time.Duration +``` +Take takes count tokens from the bucket without blocking. It returns the time +that the caller should wait until the tokens are actually available. + +Note that if the request is irrevocable - there is no way to return tokens to +the bucket once this method commits us to taking them. + +#### func (*Bucket) TakeAvailable + +```go +func (tb *Bucket) TakeAvailable(count int64) int64 +``` +TakeAvailable takes up to count immediately available tokens from the bucket. It +returns the number of tokens removed, or zero if there are no available tokens. +It does not block. + +#### func (*Bucket) TakeMaxDuration + +```go +func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool) +``` +TakeMaxDuration is like Take, except that it will only take tokens from the +bucket if the wait time for the tokens is no greater than maxWait. + +If it would take longer than maxWait for the tokens to become available, it does +nothing and reports false, otherwise it returns the time that the caller should +wait until the tokens are actually available, and reports true. + +#### func (*Bucket) Wait + +```go +func (tb *Bucket) Wait(count int64) +``` +Wait takes count tokens from the bucket, waiting until they are available. + +#### func (*Bucket) WaitMaxDuration + +```go +func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool +``` +WaitMaxDuration is like Wait except that it will only take tokens from the +bucket if it needs to wait for no greater than maxWait. It reports whether any +tokens have been removed from the bucket If no tokens have been removed, it +returns immediately. diff --git a/Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit.go b/Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit.go new file mode 100644 index 000000000..a36ffc74f --- /dev/null +++ b/Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit.go @@ -0,0 +1,221 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3 with static-linking exception. +// See LICENCE file for details. + +// The ratelimit package provides an efficient token bucket implementation +// that can be used to limit the rate of arbitrary things. +// See http://en.wikipedia.org/wiki/Token_bucket. +package ratelimit + +import ( + "strconv" + "sync" + "time" + "math" +) + +// Bucket represents a token bucket that fills at a predetermined rate. +// Methods on Bucket may be called concurrently. +type Bucket struct { + startTime time.Time + capacity int64 + quantum int64 + fillInterval time.Duration + + // The mutex guards the fields following it. + mu sync.Mutex + + // avail holds the number of available tokens + // in the bucket, as of availTick ticks from startTime. + // It will be negative when there are consumers + // waiting for tokens. + avail int64 + availTick int64 +} + +// NewBucket returns a new token bucket that fills at the +// rate of one token every fillInterval, up to the given +// maximum capacity. Both arguments must be +// positive. The bucket is initially full. +func NewBucket(fillInterval time.Duration, capacity int64) *Bucket { + return NewBucketWithQuantum(fillInterval, capacity, 1) +} + +// rateMargin specifes the allowed variance of actual +// rate from specified rate. 1% seems reasonable. +const rateMargin = 0.01 + +// NewBucketWithRate returns a token bucket that fills the bucket +// at the rate of rate tokens per second up to the given +// maximum capacity. Because of limited clock resolution, +// at high rates, the actual rate may be up to 1% different from the +// specified rate. +func NewBucketWithRate(rate float64, capacity int64) *Bucket { + for quantum := int64(1); quantum < 1<<50; quantum = nextQuantum(quantum) { + fillInterval := time.Duration(1e9 * float64(quantum) / rate) + if fillInterval <= 0 { + continue + } + tb := NewBucketWithQuantum(fillInterval, capacity, quantum) + if diff := math.Abs(tb.Rate() - rate); diff/rate <= rateMargin { + return tb + } + } + panic("cannot find suitable quantum for " + strconv.FormatFloat(rate, 'g', -1, 64)) +} + +// nextQuantum returns the next quantum to try after q. +// We grow the quantum exponentially, but slowly, so we +// get a good fit in the lower numbers. +func nextQuantum(q int64) int64 { + q1 := q * 11 / 10 + if q1 == q { + q1++ + } + return q1 +} + +// NewBucketWithQuantum is similar to NewBucket, but allows +// the specification of the quantum size - quantum tokens +// are added every fillInterval. +func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket { + if fillInterval <= 0 { + panic("token bucket fill interval is not > 0") + } + if capacity <= 0 { + panic("token bucket capacity is not > 0") + } + if quantum <= 0 { + panic("token bucket quantum is not > 0") + } + return &Bucket{ + startTime: time.Now(), + capacity: capacity, + quantum: quantum, + avail: capacity, + fillInterval: fillInterval, + } +} + +// Wait takes count tokens from the bucket, waiting until they are +// available. +func (tb *Bucket) Wait(count int64) { + if d := tb.Take(count); d > 0 { + time.Sleep(d) + } +} + +// WaitMaxDuration is like Wait except that it will +// only take tokens from the bucket if it needs to wait +// for no greater than maxWait. It reports whether +// any tokens have been removed from the bucket +// If no tokens have been removed, it returns immediately. +func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool { + d, ok := tb.TakeMaxDuration(count, maxWait) + if d > 0 { + time.Sleep(d) + } + return ok +} + +const infinityDuration time.Duration = 0x7fffffffffffffff + +// Take takes count tokens from the bucket without blocking. It returns +// the time that the caller should wait until the tokens are actually +// available. +// +// Note that if the request is irrevocable - there is no way to return +// tokens to the bucket once this method commits us to taking them. +func (tb *Bucket) Take(count int64) time.Duration { + d, _ := tb.take(time.Now(), count, infinityDuration) + return d +} + +// TakeMaxDuration is like Take, except that +// it will only take tokens from the bucket if the wait +// time for the tokens is no greater than maxWait. +// +// If it would take longer than maxWait for the tokens +// to become available, it does nothing and reports false, +// otherwise it returns the time that the caller should +// wait until the tokens are actually available, and reports +// true. +func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool) { + return tb.take(time.Now(), count, maxWait) +} + +// TakeAvailable takes up to count immediately available tokens from the +// bucket. It returns the number of tokens removed, or zero if there are +// no available tokens. It does not block. +func (tb *Bucket) TakeAvailable(count int64) int64 { + return tb.takeAvailable(time.Now(), count) +} + +// takeAvailable is the internal version of TakeAvailable - it takes the +// current time as an argument to enable easy testing. +func (tb *Bucket) takeAvailable(now time.Time, count int64) int64 { + if count <= 0 { + return 0 + } + tb.mu.Lock() + defer tb.mu.Unlock() + + tb.adjust(now) + if tb.avail <= 0 { + return 0 + } + if count > tb.avail { + count = tb.avail + } + tb.avail -= count + return count +} + +// Rate returns the fill rate of the bucket, in tokens per second. +func (tb *Bucket) Rate() float64 { + return 1e9 * float64(tb.quantum) / float64(tb.fillInterval) +} + +// take is the internal version of Take - it takes the current time as +// an argument to enable easy testing. +func (tb *Bucket) take(now time.Time, count int64, maxWait time.Duration) (time.Duration, bool) { + if count <= 0 { + return 0, true + } + tb.mu.Lock() + defer tb.mu.Unlock() + + currentTick := tb.adjust(now) + avail := tb.avail - count + if avail >= 0 { + tb.avail = avail + return 0, true + } + // Round up the missing tokens to the nearest multiple + // of quantum - the tokens won't be available until + // that tick. + endTick := currentTick + (-avail+tb.quantum-1)/tb.quantum + endTime := tb.startTime.Add(time.Duration(endTick) * tb.fillInterval) + waitTime := endTime.Sub(now) + if waitTime > maxWait { + return 0, false + } + tb.avail = avail + return waitTime, true +} + +// adjust adjusts the current bucket capacity based on the current time. +// It returns the current tick. +func (tb *Bucket) adjust(now time.Time) (currentTick int64) { + currentTick = int64(now.Sub(tb.startTime) / tb.fillInterval) + + if tb.avail >= tb.capacity { + return + } + tb.avail += (currentTick - tb.availTick) * tb.quantum + if tb.avail > tb.capacity { + tb.avail = tb.capacity + } + tb.availTick = currentTick + return +} diff --git a/Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit_test.go b/Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit_test.go new file mode 100644 index 000000000..9438a66e3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit_test.go @@ -0,0 +1,328 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3 with static-linking exception. +// See LICENCE file for details. + +package ratelimit + +import ( + gc "launchpad.net/gocheck" + + "testing" + "time" +) + +func TestPackage(t *testing.T) { + gc.TestingT(t) +} + +type rateLimitSuite struct{} + +var _ = gc.Suite(rateLimitSuite{}) + +type takeReq struct { + time time.Duration + count int64 + expectWait time.Duration +} + +var takeTests = []struct { + about string + fillInterval time.Duration + capacity int64 + reqs []takeReq +}{{ + about: "serial requests", + fillInterval: 250 * time.Millisecond, + capacity: 10, + reqs: []takeReq{{ + time: 0, + count: 0, + expectWait: 0, + }, { + time: 0, + count: 10, + expectWait: 0, + }, { + time: 0, + count: 1, + expectWait: 250 * time.Millisecond, + }, { + time: 250 * time.Millisecond, + count: 1, + expectWait: 250 * time.Millisecond, + }}, +}, { + about: "concurrent requests", + fillInterval: 250 * time.Millisecond, + capacity: 10, + reqs: []takeReq{{ + time: 0, + count: 10, + expectWait: 0, + }, { + time: 0, + count: 2, + expectWait: 500 * time.Millisecond, + }, { + time: 0, + count: 2, + expectWait: 1000 * time.Millisecond, + }, { + time: 0, + count: 1, + expectWait: 1250 * time.Millisecond, + }}, +}, { + about: "more than capacity", + fillInterval: 1 * time.Millisecond, + capacity: 10, + reqs: []takeReq{{ + time: 0, + count: 10, + expectWait: 0, + }, { + time: 20 * time.Millisecond, + count: 15, + expectWait: 5 * time.Millisecond, + }}, +}, { + about: "sub-quantum time", + fillInterval: 10 * time.Millisecond, + capacity: 10, + reqs: []takeReq{{ + time: 0, + count: 10, + expectWait: 0, + }, { + time: 7 * time.Millisecond, + count: 1, + expectWait: 3 * time.Millisecond, + }, { + time: 8 * time.Millisecond, + count: 1, + expectWait: 12 * time.Millisecond, + }}, +}, { + about: "within capacity", + fillInterval: 10 * time.Millisecond, + capacity: 5, + reqs: []takeReq{{ + time: 0, + count: 5, + expectWait: 0, + }, { + time: 60 * time.Millisecond, + count: 5, + expectWait: 0, + }, { + time: 60 * time.Millisecond, + count: 1, + expectWait: 10 * time.Millisecond, + }, { + time: 80 * time.Millisecond, + count: 2, + expectWait: 10 * time.Millisecond, + }}, +}} + +func (rateLimitSuite) TestTake(c *gc.C) { + for i, test := range takeTests { + tb := NewBucket(test.fillInterval, test.capacity) + for j, req := range test.reqs { + d, ok := tb.take(tb.startTime.Add(req.time), req.count, infinityDuration) + c.Assert(ok, gc.Equals, true) + if d != req.expectWait { + c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait) + } + } + } +} + +func (rateLimitSuite) TestTakeMaxDuration(c *gc.C) { + for i, test := range takeTests { + tb := NewBucket(test.fillInterval, test.capacity) + for j, req := range test.reqs { + if req.expectWait > 0 { + d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait-1) + c.Assert(ok, gc.Equals, false) + c.Assert(d, gc.Equals, time.Duration(0)) + } + d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait) + c.Assert(ok, gc.Equals, true) + if d != req.expectWait { + c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait) + } + } + } +} + +type takeAvailableReq struct { + time time.Duration + count int64 + expect int64 +} + +var takeAvailableTests = []struct { + about string + fillInterval time.Duration + capacity int64 + reqs []takeAvailableReq +}{{ + about: "serial requests", + fillInterval: 250 * time.Millisecond, + capacity: 10, + reqs: []takeAvailableReq{{ + time: 0, + count: 0, + expect: 0, + }, { + time: 0, + count: 10, + expect: 10, + }, { + time: 0, + count: 1, + expect: 0, + }, { + time: 250 * time.Millisecond, + count: 1, + expect: 1, + }}, +}, { + about: "concurrent requests", + fillInterval: 250 * time.Millisecond, + capacity: 10, + reqs: []takeAvailableReq{{ + time: 0, + count: 5, + expect: 5, + }, { + time: 0, + count: 2, + expect: 2, + }, { + time: 0, + count: 5, + expect: 3, + }, { + time: 0, + count: 1, + expect: 0, + }}, +}, { + about: "more than capacity", + fillInterval: 1 * time.Millisecond, + capacity: 10, + reqs: []takeAvailableReq{{ + time: 0, + count: 10, + expect: 10, + }, { + time: 20 * time.Millisecond, + count: 15, + expect: 10, + }}, +}, { + about: "within capacity", + fillInterval: 10 * time.Millisecond, + capacity: 5, + reqs: []takeAvailableReq{{ + time: 0, + count: 5, + expect: 5, + }, { + time: 60 * time.Millisecond, + count: 5, + expect: 5, + }, { + time: 70 * time.Millisecond, + count: 1, + expect: 1, + }}, +}} + +func (rateLimitSuite) TestTakeAvailable(c *gc.C) { + for i, test := range takeAvailableTests { + tb := NewBucket(test.fillInterval, test.capacity) + for j, req := range test.reqs { + d := tb.takeAvailable(tb.startTime.Add(req.time), req.count) + if d != req.expect { + c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expect) + } + } + } +} + +func (rateLimitSuite) TestPanics(c *gc.C) { + c.Assert(func() { NewBucket(0, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0") + c.Assert(func() { NewBucket(-2, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0") + c.Assert(func() { NewBucket(1, 0) }, gc.PanicMatches, "token bucket capacity is not > 0") + c.Assert(func() { NewBucket(1, -2) }, gc.PanicMatches, "token bucket capacity is not > 0") +} + +func isCloseTo(x, y, tolerance float64) bool { + return abs(x-y)/y < tolerance +} + +func (rateLimitSuite) TestRate(c *gc.C) { + tb := NewBucket(1, 1) + if !isCloseTo(tb.Rate(), 1e9, 0.00001) { + c.Fatalf("got %v want 1e9", tb.Rate()) + } + tb = NewBucket(2*time.Second, 1) + if !isCloseTo(tb.Rate(), 0.5, 0.00001) { + c.Fatalf("got %v want 0.5", tb.Rate()) + } + tb = NewBucketWithQuantum(100*time.Millisecond, 1, 5) + if !isCloseTo(tb.Rate(), 50, 0.00001) { + c.Fatalf("got %v want 50", tb.Rate()) + } +} + +func checkRate(c *gc.C, rate float64) { + tb := NewBucketWithRate(rate, 1<<62) + if !isCloseTo(tb.Rate(), rate, rateMargin) { + c.Fatalf("got %g want %v", tb.Rate(), rate) + } + d, ok := tb.take(tb.startTime, 1<<62, infinityDuration) + c.Assert(ok, gc.Equals, true) + c.Assert(d, gc.Equals, time.Duration(0)) + + // Check that the actual rate is as expected by + // asking for a not-quite multiple of the bucket's + // quantum and checking that the wait time + // correct. + d, ok = tb.take(tb.startTime, tb.quantum*2-tb.quantum/2, infinityDuration) + c.Assert(ok, gc.Equals, true) + expectTime := 1e9 * float64(tb.quantum) * 2 / rate + if !isCloseTo(float64(d), expectTime, rateMargin) { + c.Fatalf("rate %g: got %g want %v", rate, float64(d), expectTime) + } +} + +func (rateLimitSuite) TestNewWithRate(c *gc.C) { + for rate := float64(1); rate < 1e6; rate += 7 { + checkRate(c, rate) + } + for _, rate := range []float64{ + 1024 * 1024 * 1024, + 1e-5, + 0.9e-5, + 0.5, + 0.9, + 0.9e8, + 3e12, + 4e18, + } { + checkRate(c, rate) + checkRate(c, rate/3) + checkRate(c, rate*1.3) + } +} + +func BenchmarkWait(b *testing.B) { + tb := NewBucket(1, 16*1024) + for i := b.N - 1; i >= 0; i-- { + tb.Wait(1) + } +} diff --git a/Godeps/_workspace/src/github.com/juju/ratelimit/reader.go b/Godeps/_workspace/src/github.com/juju/ratelimit/reader.go new file mode 100644 index 000000000..6403bf78d --- /dev/null +++ b/Godeps/_workspace/src/github.com/juju/ratelimit/reader.go @@ -0,0 +1,51 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3 with static-linking exception. +// See LICENCE file for details. + +package ratelimit + +import "io" + +type reader struct { + r io.Reader + bucket *Bucket +} + +// Reader returns a reader that is rate limited by +// the given token bucket. Each token in the bucket +// represents one byte. +func Reader(r io.Reader, bucket *Bucket) io.Reader { + return &reader{ + r: r, + bucket: bucket, + } +} + +func (r *reader) Read(buf []byte) (int, error) { + n, err := r.r.Read(buf) + if n <= 0 { + return n, err + } + r.bucket.Wait(int64(n)) + return n, err +} + +type writer struct { + w io.Writer + bucket *Bucket +} + +// Writer returns a reader that is rate limited by +// the given token bucket. Each token in the bucket +// represents one byte. +func Writer(w io.Writer, bucket *Bucket) io.Writer { + return &writer{ + w: w, + bucket: bucket, + } +} + +func (w *writer) Write(buf []byte) (int, error) { + w.bucket.Wait(int64(len(buf))) + return w.w.Write(buf) +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/.gitignore b/Godeps/_workspace/src/github.com/lib/pq/.gitignore new file mode 100644 index 000000000..0f1d00e11 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/.gitignore @@ -0,0 +1,4 @@ +.db +*.test +*~ +*.swp diff --git a/Godeps/_workspace/src/github.com/lib/pq/.travis.yml b/Godeps/_workspace/src/github.com/lib/pq/.travis.yml new file mode 100644 index 000000000..fa3824d54 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/.travis.yml @@ -0,0 +1,62 @@ +language: go + +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - tip + +before_install: + - psql --version + - sudo /etc/init.d/postgresql stop + - sudo apt-get -y --purge remove postgresql libpq-dev libpq5 postgresql-client-common postgresql-common + - sudo rm -rf /var/lib/postgresql + - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - + - sudo sh -c "echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION >> /etc/apt/sources.list.d/postgresql.list" + - sudo apt-get update -qq + - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::="--force-confnew" install postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-contrib-$PGVERSION + - sudo chmod 777 /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "local all postgres trust" > /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "local all all trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "hostnossl all pqgossltest 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "hostnossl all pqgosslcert 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "hostssl all pqgossltest 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "hostssl all pqgosslcert 127.0.0.1/32 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "host all all 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "hostnossl all pqgossltest ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "hostnossl all pqgosslcert ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "hostssl all pqgossltest ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "hostssl all pqgosslcert ::1/128 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - echo "host all all ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf + - sudo install -o postgres -g postgres -m 600 -t /var/lib/postgresql/$PGVERSION/main/ certs/server.key certs/server.crt certs/root.crt + - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_cert_file = 'server.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)" + - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_key_file = 'server.key'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)" + - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_ca_file = 'root.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)" + - sudo sh -c "echo 127.0.0.1 postgres >> /etc/hosts" + - sudo ls -l /var/lib/postgresql/$PGVERSION/main/ + - sudo cat /etc/postgresql/$PGVERSION/main/postgresql.conf + - sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key + - sudo /etc/init.d/postgresql restart + +env: + global: + - PGUSER=postgres + - PQGOSSLTESTS=1 + - PQSSLCERTTEST_PATH=$PWD/certs + - PGHOST=127.0.0.1 + matrix: + - PGVERSION=9.4 + - PGVERSION=9.3 + - PGVERSION=9.2 + - PGVERSION=9.1 + - PGVERSION=9.0 + - PGVERSION=8.4 + +script: + - go test -v ./... + +before_script: + - psql -c 'create database pqgotest' -U postgres + - psql -c 'create user pqgossltest' -U postgres + - psql -c 'create user pqgosslcert' -U postgres diff --git a/Godeps/_workspace/src/github.com/lib/pq/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/lib/pq/CONTRIBUTING.md new file mode 100644 index 000000000..84c937f15 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/CONTRIBUTING.md @@ -0,0 +1,29 @@ +## Contributing to pq + +`pq` has a backlog of pull requests, but contributions are still very +much welcome. You can help with patch review, submitting bug reports, +or adding new functionality. There is no formal style guide, but +please conform to the style of existing code and general Go formatting +conventions when submitting patches. + +### Patch review + +Help review existing open pull requests by commenting on the code or +proposed functionality. + +### Bug reports + +We appreciate any bug reports, but especially ones with self-contained +(doesn't depend on code outside of pq), minimal (can't be simplified +further) test cases. It's especially helpful if you can submit a pull +request with just the failing test case (you'll probably want to +pattern it after the tests in +[conn_test.go](https://github.com/lib/pq/blob/master/conn_test.go). + +### New functionality + +There are a number of pending patches for new functionality, so +additional feature patches will take a while to merge. Still, patches +are generally reviewed based on usefulness and complexity in addition +to time-in-queue, so if you have a knockout idea, take a shot. Feel +free to open an issue discussion your proposed patch beforehand. diff --git a/Godeps/_workspace/src/github.com/lib/pq/LICENSE.md b/Godeps/_workspace/src/github.com/lib/pq/LICENSE.md new file mode 100644 index 000000000..5773904a3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/LICENSE.md @@ -0,0 +1,8 @@ +Copyright (c) 2011-2013, 'pq' Contributors +Portions Copyright (C) 2011 Blake Mizerany + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/lib/pq/README.md b/Godeps/_workspace/src/github.com/lib/pq/README.md new file mode 100644 index 000000000..b6e6a3248 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/README.md @@ -0,0 +1,102 @@ +# pq - A pure Go postgres driver for Go's database/sql package + +[![Build Status](https://travis-ci.org/lib/pq.png?branch=master)](https://travis-ci.org/lib/pq) + +## Install + + go get github.com/lib/pq + +## Docs + +For detailed documentation and basic usage examples, please see the package +documentation at . + +## Tests + +`go test` is used for testing. A running PostgreSQL server is +required, with the ability to log in. The default database to connect +to test with is "pqgotest," but it can be overridden using environment +variables. + +Example: + + PGHOST=/var/run/postgresql go test github.com/lib/pq + +Optionally, a benchmark suite can be run as part of the tests: + + PGHOST=/var/run/postgresql go test -bench . + +## Features + +* SSL +* Handles bad connections for `database/sql` +* Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`) +* Scan binary blobs correctly (i.e. `bytea`) +* Package for `hstore` support +* COPY FROM support +* pq.ParseURL for converting urls to connection strings for sql.Open. +* Many libpq compatible environment variables +* Unix socket support +* Notifications: `LISTEN`/`NOTIFY` + +## Future / Things you can help with + +* Better COPY FROM / COPY TO (see discussion in #181) + +## Thank you (alphabetical) + +Some of these contributors are from the original library `bmizerany/pq.go` whose +code still exists in here. + +* Andy Balholm (andybalholm) +* Ben Berkert (benburkert) +* Benjamin Heatwole (bheatwole) +* Bill Mill (llimllib) +* Bjørn Madsen (aeons) +* Blake Gentry (bgentry) +* Brad Fitzpatrick (bradfitz) +* Charlie Melbye (cmelbye) +* Chris Bandy (cbandy) +* Chris Walsh (cwds) +* Dan Sosedoff (sosedoff) +* Daniel Farina (fdr) +* Eric Chlebek (echlebek) +* Eric Garrido (minusnine) +* Eric Urban (hydrogen18) +* Everyone at The Go Team +* Evan Shaw (edsrzf) +* Ewan Chou (coocood) +* Federico Romero (federomero) +* Fumin (fumin) +* Gary Burd (garyburd) +* Heroku (heroku) +* James Pozdena (jpoz) +* Jason McVetta (jmcvetta) +* Jeremy Jay (pbnjay) +* Joakim Sernbrant (serbaut) +* John Gallagher (jgallagher) +* Jonathan Rudenberg (titanous) +* Joël Stemmer (jstemmer) +* Kamil Kisiel (kisielk) +* Kelly Dunn (kellydunn) +* Keith Rarick (kr) +* Kir Shatrov (kirs) +* Lann Martin (lann) +* Maciek Sakrejda (deafbybeheading) +* Marc Brinkmann (mbr) +* Marko Tiikkaja (johto) +* Matt Newberry (MattNewberry) +* Matt Robenolt (mattrobenolt) +* Martin Olsen (martinolsen) +* Mike Lewis (mikelikespie) +* Nicolas Patry (Narsil) +* Oliver Tonnhofer (olt) +* Patrick Hayes (phayes) +* Paul Hammond (paulhammond) +* Ryan Smith (ryandotsmith) +* Samuel Stauffer (samuel) +* Timothée Peignier (cyberdelia) +* Travis Cline (tmc) +* TruongSinh Tran-Nguyen (truongsinh) +* Yaismel Miranda (ympons) +* notedit (notedit) diff --git a/Godeps/_workspace/src/github.com/lib/pq/bench_test.go b/Godeps/_workspace/src/github.com/lib/pq/bench_test.go new file mode 100644 index 000000000..611edf87f --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/bench_test.go @@ -0,0 +1,435 @@ +// +build go1.1 + +package pq + +import ( + "bufio" + "bytes" + "database/sql" + "database/sql/driver" + "io" + "math/rand" + "net" + "runtime" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/lib/pq/oid" +) + +var ( + selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'" + selectSeriesQuery = "SELECT generate_series(1, 100)" +) + +func BenchmarkSelectString(b *testing.B) { + var result string + benchQuery(b, selectStringQuery, &result) +} + +func BenchmarkSelectSeries(b *testing.B) { + var result int + benchQuery(b, selectSeriesQuery, &result) +} + +func benchQuery(b *testing.B, query string, result interface{}) { + b.StopTimer() + db := openTestConn(b) + defer db.Close() + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchQueryLoop(b, db, query, result) + } +} + +func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) { + rows, err := db.Query(query) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + for rows.Next() { + err = rows.Scan(result) + if err != nil { + b.Fatal("failed to scan", err) + } + } +} + +// reading from circularConn yields content[:prefixLen] once, followed by +// content[prefixLen:] over and over again. It never returns EOF. +type circularConn struct { + content string + prefixLen int + pos int + net.Conn // for all other net.Conn methods that will never be called +} + +func (r *circularConn) Read(b []byte) (n int, err error) { + n = copy(b, r.content[r.pos:]) + r.pos += n + if r.pos >= len(r.content) { + r.pos = r.prefixLen + } + return +} + +func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil } + +func (r *circularConn) Close() error { return nil } + +func fakeConn(content string, prefixLen int) *conn { + c := &circularConn{content: content, prefixLen: prefixLen} + return &conn{buf: bufio.NewReader(c), c: c} +} + +// This benchmark is meant to be the same as BenchmarkSelectString, but takes +// out some of the factors this package can't control. The numbers are less noisy, +// but also the costs of network communication aren't accurately represented. +func BenchmarkMockSelectString(b *testing.B) { + b.StopTimer() + // taken from a recorded run of BenchmarkSelectString + // See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html + const response = "1\x00\x00\x00\x04" + + "t\x00\x00\x00\x06\x00\x00" + + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + + "Z\x00\x00\x00\x05I" + + "2\x00\x00\x00\x04" + + "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + + "C\x00\x00\x00\rSELECT 1\x00" + + "Z\x00\x00\x00\x05I" + + "3\x00\x00\x00\x04" + + "Z\x00\x00\x00\x05I" + c := fakeConn(response, 0) + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchMockQuery(b, c, selectStringQuery) + } +} + +var seriesRowData = func() string { + var buf bytes.Buffer + for i := 1; i <= 100; i++ { + digits := byte(2) + if i >= 100 { + digits = 3 + } else if i < 10 { + digits = 1 + } + buf.WriteString("D\x00\x00\x00") + buf.WriteByte(10 + digits) + buf.WriteString("\x00\x01\x00\x00\x00") + buf.WriteByte(digits) + buf.WriteString(strconv.Itoa(i)) + } + return buf.String() +}() + +func BenchmarkMockSelectSeries(b *testing.B) { + b.StopTimer() + var response = "1\x00\x00\x00\x04" + + "t\x00\x00\x00\x06\x00\x00" + + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + + "Z\x00\x00\x00\x05I" + + "2\x00\x00\x00\x04" + + seriesRowData + + "C\x00\x00\x00\x0fSELECT 100\x00" + + "Z\x00\x00\x00\x05I" + + "3\x00\x00\x00\x04" + + "Z\x00\x00\x00\x05I" + c := fakeConn(response, 0) + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchMockQuery(b, c, selectSeriesQuery) + } +} + +func benchMockQuery(b *testing.B, c *conn, query string) { + stmt, err := c.Prepare(query) + if err != nil { + b.Fatal(err) + } + defer stmt.Close() + rows, err := stmt.Query(nil) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + var dest [1]driver.Value + for { + if err := rows.Next(dest[:]); err != nil { + if err == io.EOF { + break + } + b.Fatal(err) + } + } +} + +func BenchmarkPreparedSelectString(b *testing.B) { + var result string + benchPreparedQuery(b, selectStringQuery, &result) +} + +func BenchmarkPreparedSelectSeries(b *testing.B) { + var result int + benchPreparedQuery(b, selectSeriesQuery, &result) +} + +func benchPreparedQuery(b *testing.B, query string, result interface{}) { + b.StopTimer() + db := openTestConn(b) + defer db.Close() + stmt, err := db.Prepare(query) + if err != nil { + b.Fatal(err) + } + defer stmt.Close() + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchPreparedQueryLoop(b, db, stmt, result) + } +} + +func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) { + rows, err := stmt.Query() + if err != nil { + b.Fatal(err) + } + if !rows.Next() { + rows.Close() + b.Fatal("no rows") + } + defer rows.Close() + for rows.Next() { + err = rows.Scan(&result) + if err != nil { + b.Fatal("failed to scan") + } + } +} + +// See the comment for BenchmarkMockSelectString. +func BenchmarkMockPreparedSelectString(b *testing.B) { + b.StopTimer() + const parseResponse = "1\x00\x00\x00\x04" + + "t\x00\x00\x00\x06\x00\x00" + + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + + "Z\x00\x00\x00\x05I" + const responses = parseResponse + + "2\x00\x00\x00\x04" + + "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + + "C\x00\x00\x00\rSELECT 1\x00" + + "Z\x00\x00\x00\x05I" + c := fakeConn(responses, len(parseResponse)) + + stmt, err := c.Prepare(selectStringQuery) + if err != nil { + b.Fatal(err) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchPreparedMockQuery(b, c, stmt) + } +} + +func BenchmarkMockPreparedSelectSeries(b *testing.B) { + b.StopTimer() + const parseResponse = "1\x00\x00\x00\x04" + + "t\x00\x00\x00\x06\x00\x00" + + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + + "Z\x00\x00\x00\x05I" + var responses = parseResponse + + "2\x00\x00\x00\x04" + + seriesRowData + + "C\x00\x00\x00\x0fSELECT 100\x00" + + "Z\x00\x00\x00\x05I" + c := fakeConn(responses, len(parseResponse)) + + stmt, err := c.Prepare(selectSeriesQuery) + if err != nil { + b.Fatal(err) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchPreparedMockQuery(b, c, stmt) + } +} + +func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) { + rows, err := stmt.Query(nil) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + var dest [1]driver.Value + for { + if err := rows.Next(dest[:]); err != nil { + if err == io.EOF { + break + } + b.Fatal(err) + } + } +} + +func BenchmarkEncodeInt64(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{}, int64(1234), oid.T_int8) + } +} + +func BenchmarkEncodeFloat64(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{}, 3.14159, oid.T_float8) + } +} + +var testByteString = []byte("abcdefghijklmnopqrstuvwxyz") + +func BenchmarkEncodeByteaHex(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{serverVersion: 90000}, testByteString, oid.T_bytea) + } +} +func BenchmarkEncodeByteaEscape(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{serverVersion: 84000}, testByteString, oid.T_bytea) + } +} + +func BenchmarkEncodeBool(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{}, true, oid.T_bool) + } +} + +var testTimestamptz = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.Local) + +func BenchmarkEncodeTimestamptz(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{}, testTimestamptz, oid.T_timestamptz) + } +} + +var testIntBytes = []byte("1234") + +func BenchmarkDecodeInt64(b *testing.B) { + for i := 0; i < b.N; i++ { + decode(¶meterStatus{}, testIntBytes, oid.T_int8) + } +} + +var testFloatBytes = []byte("3.14159") + +func BenchmarkDecodeFloat64(b *testing.B) { + for i := 0; i < b.N; i++ { + decode(¶meterStatus{}, testFloatBytes, oid.T_float8) + } +} + +var testBoolBytes = []byte{'t'} + +func BenchmarkDecodeBool(b *testing.B) { + for i := 0; i < b.N; i++ { + decode(¶meterStatus{}, testBoolBytes, oid.T_bool) + } +} + +func TestDecodeBool(t *testing.T) { + db := openTestConn(t) + rows, err := db.Query("select true") + if err != nil { + t.Fatal(err) + } + rows.Close() +} + +var testTimestamptzBytes = []byte("2013-09-17 22:15:32.360754-07") + +func BenchmarkDecodeTimestamptz(b *testing.B) { + for i := 0; i < b.N; i++ { + decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz) + } +} + +func BenchmarkDecodeTimestamptzMultiThread(b *testing.B) { + oldProcs := runtime.GOMAXPROCS(0) + defer runtime.GOMAXPROCS(oldProcs) + runtime.GOMAXPROCS(runtime.NumCPU()) + globalLocationCache = newLocationCache() + + f := func(wg *sync.WaitGroup, loops int) { + defer wg.Done() + for i := 0; i < loops; i++ { + decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz) + } + } + + wg := &sync.WaitGroup{} + b.ResetTimer() + for j := 0; j < 10; j++ { + wg.Add(1) + go f(wg, b.N/10) + } + wg.Wait() +} + +func BenchmarkLocationCache(b *testing.B) { + globalLocationCache = newLocationCache() + for i := 0; i < b.N; i++ { + globalLocationCache.getLocation(rand.Intn(10000)) + } +} + +func BenchmarkLocationCacheMultiThread(b *testing.B) { + oldProcs := runtime.GOMAXPROCS(0) + defer runtime.GOMAXPROCS(oldProcs) + runtime.GOMAXPROCS(runtime.NumCPU()) + globalLocationCache = newLocationCache() + + f := func(wg *sync.WaitGroup, loops int) { + defer wg.Done() + for i := 0; i < loops; i++ { + globalLocationCache.getLocation(rand.Intn(10000)) + } + } + + wg := &sync.WaitGroup{} + b.ResetTimer() + for j := 0; j < 10; j++ { + wg.Add(1) + go f(wg, b.N/10) + } + wg.Wait() +} + +// Stress test the performance of parsing results from the wire. +func BenchmarkResultParsing(b *testing.B) { + b.StopTimer() + + db := openTestConn(b) + defer db.Close() + _, err := db.Exec("BEGIN") + if err != nil { + b.Fatal(err) + } + + b.StartTimer() + for i := 0; i < b.N; i++ { + res, err := db.Query("SELECT generate_series(1, 50000)") + if err != nil { + b.Fatal(err) + } + res.Close() + } +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/buf.go b/Godeps/_workspace/src/github.com/lib/pq/buf.go new file mode 100644 index 000000000..fd966c394 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/buf.go @@ -0,0 +1,74 @@ +package pq + +import ( + "bytes" + "encoding/binary" + + "github.com/lib/pq/oid" +) + +type readBuf []byte + +func (b *readBuf) int32() (n int) { + n = int(int32(binary.BigEndian.Uint32(*b))) + *b = (*b)[4:] + return +} + +func (b *readBuf) oid() (n oid.Oid) { + n = oid.Oid(binary.BigEndian.Uint32(*b)) + *b = (*b)[4:] + return +} + +func (b *readBuf) int16() (n int) { + n = int(binary.BigEndian.Uint16(*b)) + *b = (*b)[2:] + return +} + +func (b *readBuf) string() string { + i := bytes.IndexByte(*b, 0) + if i < 0 { + errorf("invalid message format; expected string terminator") + } + s := (*b)[:i] + *b = (*b)[i+1:] + return string(s) +} + +func (b *readBuf) next(n int) (v []byte) { + v = (*b)[:n] + *b = (*b)[n:] + return +} + +func (b *readBuf) byte() byte { + return b.next(1)[0] +} + +type writeBuf []byte + +func (b *writeBuf) int32(n int) { + x := make([]byte, 4) + binary.BigEndian.PutUint32(x, uint32(n)) + *b = append(*b, x...) +} + +func (b *writeBuf) int16(n int) { + x := make([]byte, 2) + binary.BigEndian.PutUint16(x, uint16(n)) + *b = append(*b, x...) +} + +func (b *writeBuf) string(s string) { + *b = append(*b, (s + "\000")...) +} + +func (b *writeBuf) byte(c byte) { + *b = append(*b, c) +} + +func (b *writeBuf) bytes(v []byte) { + *b = append(*b, v...) +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/certs/README b/Godeps/_workspace/src/github.com/lib/pq/certs/README new file mode 100644 index 000000000..24ab7b256 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/certs/README @@ -0,0 +1,3 @@ +This directory contains certificates and private keys for testing some +SSL-related functionality in Travis. Do NOT use these certificates for +anything other than testing. diff --git a/Godeps/_workspace/src/github.com/lib/pq/certs/postgresql.crt b/Godeps/_workspace/src/github.com/lib/pq/certs/postgresql.crt new file mode 100644 index 000000000..6e6b4284a --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/certs/postgresql.crt @@ -0,0 +1,69 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pq CA + Validity + Not Before: Oct 11 15:10:11 2014 GMT + Not After : Oct 8 15:10:11 2024 GMT + Subject: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pqgosslcert + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:e3:8c:06:9a:70:54:51:d1:34:34:83:39:cd:a2: + 59:0f:05:ed:8d:d8:0e:34:d0:92:f4:09:4d:ee:8c: + 78:55:49:24:f8:3c:e0:34:58:02:b2:e7:94:58:c1: + e8:e5:bb:d1:af:f6:54:c1:40:b1:90:70:79:0d:35: + 54:9c:8f:16:e9:c2:f0:92:e6:64:49:38:c1:76:f8: + 47:66:c4:5b:4a:b6:a9:43:ce:c8:be:6c:4d:2b:94: + 97:3c:55:bc:d1:d0:6e:b7:53:ae:89:5c:4b:6b:86: + 40:be:c1:ae:1e:64:ce:9c:ae:87:0a:69:e5:c8:21: + 12:be:ae:1d:f6:45:df:16:a7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 9B:25:31:63:A2:D8:06:FF:CB:E3:E9:96:FF:0D:BA:DC:12:7D:04:CF + X509v3 Authority Key Identifier: + keyid:52:93:ED:1E:76:0A:9F:65:4F:DE:19:66:C1:D5:22:40:35:CB:A0:72 + + X509v3 Basic Constraints: + CA:FALSE + X509v3 Key Usage: + Digital Signature, Non Repudiation, Key Encipherment + Signature Algorithm: sha256WithRSAEncryption + 3e:f5:f8:0b:4e:11:bd:00:86:1f:ce:dc:97:02:98:91:11:f5: + 65:f6:f2:8a:b2:3e:47:92:05:69:28:c9:e9:b4:f7:cf:93:d1: + 2d:81:5d:00:3c:23:be:da:70:ea:59:e1:2c:d3:25:49:ae:a6: + 95:54:c1:10:df:23:e3:fe:d6:e4:76:c7:6b:73:ad:1b:34:7c: + e2:56:cc:c0:37:ae:c5:7a:11:20:6c:3d:05:0e:99:cd:22:6c: + cf:59:a1:da:28:d4:65:ba:7d:2f:2b:3d:69:6d:a6:c1:ae:57: + bf:56:64:13:79:f8:48:46:65:eb:81:67:28:0b:7b:de:47:10: + b3:80:3c:31:d1:58:94:01:51:4a:c7:c8:1a:01:a8:af:c4:cd: + bb:84:a5:d9:8b:b4:b9:a1:64:3e:95:d9:90:1d:d5:3f:67:cc: + 3b:ba:f5:b4:d1:33:77:ee:c2:d2:3e:7e:c5:66:6e:b7:35:4c: + 60:57:b0:b8:be:36:c8:f3:d3:95:8c:28:4a:c9:f7:27:a4:0d: + e5:96:99:eb:f5:c8:bd:f3:84:6d:ef:02:f9:8a:36:7d:6b:5f: + 36:68:37:41:d9:74:ae:c6:78:2e:44:86:a1:ad:43:ca:fb:b5: + 3e:ba:10:23:09:02:ac:62:d1:d0:83:c8:95:b9:e3:5e:30:ff: + 5b:2b:38:fa +-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIBAjANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEP +MA0GA1UECBMGTmV2YWRhMRIwEAYDVQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdp +dGh1Yi5jb20vbGliL3BxMQ4wDAYDVQQDEwVwcSBDQTAeFw0xNDEwMTExNTEwMTFa +Fw0yNDEwMDgxNTEwMTFaMGQxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGEx +EjAQBgNVBAcTCUxhcyBWZWdhczEaMBgGA1UEChMRZ2l0aHViLmNvbS9saWIvcHEx +FDASBgNVBAMTC3BxZ29zc2xjZXJ0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQDjjAaacFRR0TQ0gznNolkPBe2N2A400JL0CU3ujHhVSST4POA0WAKy55RYwejl +u9Gv9lTBQLGQcHkNNVScjxbpwvCS5mRJOMF2+EdmxFtKtqlDzsi+bE0rlJc8VbzR +0G63U66JXEtrhkC+wa4eZM6crocKaeXIIRK+rh32Rd8WpwIDAQABo1owWDAdBgNV +HQ4EFgQUmyUxY6LYBv/L4+mW/w263BJ9BM8wHwYDVR0jBBgwFoAUUpPtHnYKn2VP +3hlmwdUiQDXLoHIwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL +BQADggEBAD71+AtOEb0Ahh/O3JcCmJER9WX28oqyPkeSBWkoyem098+T0S2BXQA8 +I77acOpZ4SzTJUmuppVUwRDfI+P+1uR2x2tzrRs0fOJWzMA3rsV6ESBsPQUOmc0i +bM9Zodoo1GW6fS8rPWltpsGuV79WZBN5+EhGZeuBZygLe95HELOAPDHRWJQBUUrH +yBoBqK/EzbuEpdmLtLmhZD6V2ZAd1T9nzDu69bTRM3fuwtI+fsVmbrc1TGBXsLi+ +Nsjz05WMKErJ9yekDeWWmev1yL3zhG3vAvmKNn1rXzZoN0HZdK7GeC5EhqGtQ8r7 +tT66ECMJAqxi0dCDyJW5414w/1srOPo= +-----END CERTIFICATE----- diff --git a/Godeps/_workspace/src/github.com/lib/pq/certs/postgresql.key b/Godeps/_workspace/src/github.com/lib/pq/certs/postgresql.key new file mode 100644 index 000000000..eb8b20be9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/certs/postgresql.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDjjAaacFRR0TQ0gznNolkPBe2N2A400JL0CU3ujHhVSST4POA0 +WAKy55RYwejlu9Gv9lTBQLGQcHkNNVScjxbpwvCS5mRJOMF2+EdmxFtKtqlDzsi+ +bE0rlJc8VbzR0G63U66JXEtrhkC+wa4eZM6crocKaeXIIRK+rh32Rd8WpwIDAQAB +AoGAM5dM6/kp9P700i8qjOgRPym96Zoh5nGfz/rIE5z/r36NBkdvIg8OVZfR96nH +b0b9TOMR5lsPp0sI9yivTWvX6qyvLJRWy2vvx17hXK9NxXUNTAm0PYZUTvCtcPeX +RnJpzQKNZQPkFzF0uXBc4CtPK2Vz0+FGvAelrhYAxnw1dIkCQQD+9qaW5QhXjsjb +Nl85CmXgxPmGROcgLQCO+omfrjf9UXrituU9Dz6auym5lDGEdMFnkzfr+wpasEy9 +mf5ZZOhDAkEA5HjXfVGaCtpydOt6hDon/uZsyssCK2lQ7NSuE3vP+sUsYMzIpEoy +t3VWXqKbo+g9KNDTP4WEliqp1aiSIylzzQJANPeqzihQnlgEdD4MdD4rwhFJwVIp +Le8Lcais1KaN7StzOwxB/XhgSibd2TbnPpw+3bSg5n5lvUdo+e62/31OHwJAU1jS +I+F09KikQIr28u3UUWT2IzTT4cpVv1AHAQyV3sG3YsjSGT0IK20eyP9BEBZU2WL0 +7aNjrvR5aHxKc5FXsQJABsFtyGpgI5X4xufkJZVZ+Mklz2n7iXa+XPatMAHFxAtb +EEMt60rngwMjXAzBSC6OYuYogRRAY3UCacNC5VhLYQ== +-----END RSA PRIVATE KEY----- diff --git a/Godeps/_workspace/src/github.com/lib/pq/certs/root.crt b/Godeps/_workspace/src/github.com/lib/pq/certs/root.crt new file mode 100644 index 000000000..aecf8f621 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/certs/root.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIJANmheROCdW1NMA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNV +BAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGExEjAQBgNVBAcTCUxhcyBWZWdhczEaMBgG +A1UEChMRZ2l0aHViLmNvbS9saWIvcHExDjAMBgNVBAMTBXBxIENBMB4XDTE0MTAx +MTE1MDQyOVoXDTI0MTAwODE1MDQyOVowXjELMAkGA1UEBhMCVVMxDzANBgNVBAgT +Bk5ldmFkYTESMBAGA1UEBxMJTGFzIFZlZ2FzMRowGAYDVQQKExFnaXRodWIuY29t +L2xpYi9wcTEOMAwGA1UEAxMFcHEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCV4PxP7ShzWBzUCThcKk3qZtOLtHmszQVtbqhvgTpm1kTRtKBdVMu0 +pLAHQ3JgJCnAYgH0iZxVGoMP16T3irdgsdC48+nNTFM2T0cCdkfDURGIhSFN47cb +Pgy306BcDUD2q7ucW33+dlFSRuGVewocoh4BWM/vMtMvvWzdi4Ag/L/jhb+5wZxZ +sWymsadOVSDePEMKOvlCa3EdVwVFV40TVyDb+iWBUivDAYsS2a3KajuJrO6MbZiE +Sp2RCIkZS2zFmzWxVRi9ZhzIZhh7EVF9JAaNC3T52jhGUdlRq3YpBTMnd89iOh74 +6jWXG7wSuPj3haFzyNhmJ0ZUh+2Ynoh1AgMBAAGjgcMwgcAwHQYDVR0OBBYEFFKT +7R52Cp9lT94ZZsHVIkA1y6ByMIGQBgNVHSMEgYgwgYWAFFKT7R52Cp9lT94ZZsHV +IkA1y6ByoWKkYDBeMQswCQYDVQQGEwJVUzEPMA0GA1UECBMGTmV2YWRhMRIwEAYD +VQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdpdGh1Yi5jb20vbGliL3BxMQ4wDAYD +VQQDEwVwcSBDQYIJANmheROCdW1NMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF +BQADggEBAAEhCLWkqJNMI8b4gkbmj5fqQ/4+oO83bZ3w2Oqf6eZ8I8BC4f2NOyE6 +tRUlq5+aU7eqC1cOAvGjO+YHN/bF/DFpwLlzvUSXt+JP/pYcUjL7v+pIvwqec9hD +ndvM4iIbkD/H/OYQ3L+N3W+G1x7AcFIX+bGCb3PzYVQAjxreV6//wgKBosMGFbZo +HPxT9RPMun61SViF04H5TNs0derVn1+5eiiYENeAhJzQNyZoOOUuX1X/Inx9bEPh +C5vFBtSMgIytPgieRJVWAiMLYsfpIAStrHztRAbBs2DU01LmMgRvHdxgFEKinC/d +UHZZQDP+6pT+zADrGhQGXe4eThaO6f0= +-----END CERTIFICATE----- diff --git a/Godeps/_workspace/src/github.com/lib/pq/certs/server.crt b/Godeps/_workspace/src/github.com/lib/pq/certs/server.crt new file mode 100644 index 000000000..ddc995a6d --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/certs/server.crt @@ -0,0 +1,81 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pq CA + Validity + Not Before: Oct 11 15:05:15 2014 GMT + Not After : Oct 8 15:05:15 2024 GMT + Subject: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=postgres + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:d7:8a:4c:85:fb:17:a5:3c:8f:e0:72:11:29:ce: + 3f:b0:1f:3f:7d:c6:ee:7f:a7:fc:02:2b:35:47:08: + a6:3d:90:df:5c:56:14:94:00:c7:6d:d1:d2:e2:61: + 95:77:b8:e3:a6:66:31:f9:1f:21:7d:62:e1:27:da: + 94:37:61:4a:ea:63:53:a0:61:b8:9c:bb:a5:e2:e7: + b7:a6:d8:0f:05:04:c7:29:e2:ea:49:2b:7f:de:15: + 00:a6:18:70:50:c7:0c:de:9a:f9:5a:96:b0:e1:94: + 06:c6:6d:4a:21:3b:b4:0f:a5:6d:92:86:34:b2:4e: + d7:0e:a7:19:c0:77:0b:7b:87:c8:92:de:42:ff:86: + d2:b7:9a:a4:d4:15:23:ca:ad:a5:69:21:b8:ce:7e: + 66:cb:85:5d:b9:ed:8b:2d:09:8d:94:e4:04:1e:72: + ec:ef:d0:76:90:15:5a:a4:f7:91:4b:e9:ce:4e:9d: + 5d:9a:70:17:9c:d8:e9:73:83:ea:3d:61:99:a6:cd: + ac:91:40:5a:88:77:e5:4e:2a:8e:3d:13:f3:f9:38: + 6f:81:6b:8a:95:ca:0e:07:ab:6f:da:b4:8c:d9:ff: + aa:78:03:aa:c7:c2:cf:6f:64:92:d3:d8:83:d5:af: + f1:23:18:a7:2e:7b:17:0b:e7:7d:f1:fa:a8:41:a3: + 04:57 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + EE:F0:B3:46:DC:C7:09:EB:0E:B6:2F:E5:FE:62:60:45:44:9F:59:CC + X509v3 Authority Key Identifier: + keyid:52:93:ED:1E:76:0A:9F:65:4F:DE:19:66:C1:D5:22:40:35:CB:A0:72 + + X509v3 Basic Constraints: + CA:FALSE + X509v3 Key Usage: + Digital Signature, Non Repudiation, Key Encipherment + Signature Algorithm: sha256WithRSAEncryption + 7e:5a:6e:be:bf:d2:6c:c1:d6:fa:b6:fb:3f:06:53:36:08:87: + 9d:95:b1:39:af:9e:f6:47:38:17:39:da:25:7c:f2:ad:0c:e3: + ab:74:19:ca:fb:8c:a0:50:c0:1d:19:8a:9c:21:ed:0f:3a:d1: + 96:54:2e:10:09:4f:b8:70:f7:2b:99:43:d2:c6:15:bc:3f:24: + 7d:28:39:32:3f:8d:a4:4f:40:75:7f:3e:0d:1c:d1:69:f2:4e: + 98:83:47:97:d2:25:ac:c9:36:86:2f:04:a6:c4:86:c7:c4:00: + 5f:7f:b9:ad:fc:bf:e9:f5:78:d7:82:1a:51:0d:fc:ab:9e:92: + 1d:5f:0c:18:d1:82:e0:14:c9:ce:91:89:71:ff:49:49:ff:35: + bf:7b:44:78:42:c1:d0:66:65:bb:28:2e:60:ca:9b:20:12:a9: + 90:61:b1:96:ec:15:46:c9:37:f7:07:90:8a:89:45:2a:3f:37: + ec:dc:e3:e5:8f:c3:3a:57:80:a5:54:60:0c:e1:b2:26:99:2b: + 40:7e:36:d1:9a:70:02:ec:63:f4:3b:72:ae:81:fb:30:20:6d: + cb:48:46:c6:b5:8f:39:b1:84:05:25:55:8d:f5:62:f6:1b:46: + 2e:da:a3:4c:26:12:44:d7:56:b6:b8:a9:ca:d3:ab:71:45:7c: + 9f:48:6d:1e +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIBATANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEP +MA0GA1UECBMGTmV2YWRhMRIwEAYDVQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdp +dGh1Yi5jb20vbGliL3BxMQ4wDAYDVQQDEwVwcSBDQTAeFw0xNDEwMTExNTA1MTVa +Fw0yNDEwMDgxNTA1MTVaMGExCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGEx +EjAQBgNVBAcTCUxhcyBWZWdhczEaMBgGA1UEChMRZ2l0aHViLmNvbS9saWIvcHEx +ETAPBgNVBAMTCHBvc3RncmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA14pMhfsXpTyP4HIRKc4/sB8/fcbuf6f8Ais1RwimPZDfXFYUlADHbdHS4mGV +d7jjpmYx+R8hfWLhJ9qUN2FK6mNToGG4nLul4ue3ptgPBQTHKeLqSSt/3hUAphhw +UMcM3pr5Wpaw4ZQGxm1KITu0D6VtkoY0sk7XDqcZwHcLe4fIkt5C/4bSt5qk1BUj +yq2laSG4zn5my4Vdue2LLQmNlOQEHnLs79B2kBVapPeRS+nOTp1dmnAXnNjpc4Pq +PWGZps2skUBaiHflTiqOPRPz+ThvgWuKlcoOB6tv2rSM2f+qeAOqx8LPb2SS09iD +1a/xIxinLnsXC+d98fqoQaMEVwIDAQABo1owWDAdBgNVHQ4EFgQU7vCzRtzHCesO +ti/l/mJgRUSfWcwwHwYDVR0jBBgwFoAUUpPtHnYKn2VP3hlmwdUiQDXLoHIwCQYD +VR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQELBQADggEBAH5abr6/0mzB +1vq2+z8GUzYIh52VsTmvnvZHOBc52iV88q0M46t0Gcr7jKBQwB0Zipwh7Q860ZZU +LhAJT7hw9yuZQ9LGFbw/JH0oOTI/jaRPQHV/Pg0c0WnyTpiDR5fSJazJNoYvBKbE +hsfEAF9/ua38v+n1eNeCGlEN/Kuekh1fDBjRguAUyc6RiXH/SUn/Nb97RHhCwdBm +ZbsoLmDKmyASqZBhsZbsFUbJN/cHkIqJRSo/N+zc4+WPwzpXgKVUYAzhsiaZK0B+ +NtGacALsY/Q7cq6B+zAgbctIRsa1jzmxhAUlVY31YvYbRi7ao0wmEkTXVra4qcrT +q3FFfJ9IbR4= +-----END CERTIFICATE----- diff --git a/Godeps/_workspace/src/github.com/lib/pq/certs/server.key b/Godeps/_workspace/src/github.com/lib/pq/certs/server.key new file mode 100644 index 000000000..bd7b019b6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/certs/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA14pMhfsXpTyP4HIRKc4/sB8/fcbuf6f8Ais1RwimPZDfXFYU +lADHbdHS4mGVd7jjpmYx+R8hfWLhJ9qUN2FK6mNToGG4nLul4ue3ptgPBQTHKeLq +SSt/3hUAphhwUMcM3pr5Wpaw4ZQGxm1KITu0D6VtkoY0sk7XDqcZwHcLe4fIkt5C +/4bSt5qk1BUjyq2laSG4zn5my4Vdue2LLQmNlOQEHnLs79B2kBVapPeRS+nOTp1d +mnAXnNjpc4PqPWGZps2skUBaiHflTiqOPRPz+ThvgWuKlcoOB6tv2rSM2f+qeAOq +x8LPb2SS09iD1a/xIxinLnsXC+d98fqoQaMEVwIDAQABAoIBAF3ZoihUhJ82F4+r +Gz4QyDpv4L1reT2sb1aiabhcU8ZK5nbWJG+tRyjSS/i2dNaEcttpdCj9HR/zhgZM +bm0OuAgG58rVwgS80CZUruq++Qs+YVojq8/gWPTiQD4SNhV2Fmx3HkwLgUk3oxuT +SsvdqzGE3okGVrutCIcgy126eA147VPMoej1Bb3fO6npqK0pFPhZfAc0YoqJuM+k +obRm5pAnGUipyLCFXjA9HYPKwYZw2RtfdA3CiImHeanSdqS+ctrC9y8BV40Th7gZ +haXdKUNdjmIxV695QQ1mkGqpKLZFqhzKioGQ2/Ly2d1iaKN9fZltTusu8unepWJ2 +tlT9qMECgYEA9uHaF1t2CqE+AJvWTihHhPIIuLxoOQXYea1qvxfcH/UMtaLKzCNm +lQ5pqCGsPvp+10f36yttO1ZehIvlVNXuJsjt0zJmPtIolNuJY76yeussfQ9jHheB +5uPEzCFlHzxYbBUyqgWaF6W74okRGzEGJXjYSP0yHPPdU4ep2q3bGiUCgYEA34Af +wBSuQSK7uLxArWHvQhyuvi43ZGXls6oRGl+Ysj54s8BP6XGkq9hEJ6G4yxgyV+BR +DUOs5X8/TLT8POuIMYvKTQthQyCk0eLv2FLdESDuuKx0kBVY3s8lK3/z5HhrdOiN +VMNZU+xDKgKc3hN9ypkk8vcZe6EtH7Y14e0rVcsCgYBTgxi8F/M5K0wG9rAqphNz +VFBA9XKn/2M33cKjO5X5tXIEKzpAjaUQvNxexG04rJGljzG8+mar0M6ONahw5yD1 +O7i/XWgazgpuOEkkVYiYbd8RutfDgR4vFVMn3hAP3eDnRtBplRWH9Ec3HTiNIys6 +F8PKBOQjyRZQQC7jyzW3hQKBgACe5HeuFwXLSOYsb6mLmhR+6+VPT4wR1F95W27N +USk9jyxAnngxfpmTkiziABdgS9N+pfr5cyN4BP77ia/Jn6kzkC5Cl9SN5KdIkA3z +vPVtN/x/ThuQU5zaymmig1ThGLtMYggYOslG4LDfLPxY5YKIhle+Y+259twdr2yf +Mf2dAoGAaGv3tWMgnIdGRk6EQL/yb9PKHo7ShN+tKNlGaK7WwzBdKs+Fe8jkgcr7 +pz4Ne887CmxejdISzOCcdT+Zm9Bx6I/uZwWOtDvWpIgIxVX9a9URj/+D1MxTE/y4 +d6H+c89yDY62I2+drMpdjCd3EtCaTlxpTbRS+s1eAHMH7aEkcCE= +-----END RSA PRIVATE KEY----- diff --git a/Godeps/_workspace/src/github.com/lib/pq/conn.go b/Godeps/_workspace/src/github.com/lib/pq/conn.go new file mode 100644 index 000000000..e92798eda --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/conn.go @@ -0,0 +1,1490 @@ +package pq + +import ( + "bufio" + "crypto/md5" + "crypto/tls" + "crypto/x509" + "database/sql" + "database/sql/driver" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "os" + "os/user" + "path" + "path/filepath" + "strconv" + "strings" + "time" + "unicode" + + "github.com/lib/pq/oid" +) + +// Common error types +var ( + ErrNotSupported = errors.New("pq: Unsupported command") + ErrInFailedTransaction = errors.New("pq: Could not complete operation in a failed transaction") + ErrSSLNotSupported = errors.New("pq: SSL is not enabled on the server") + ErrSSLKeyHasWorldPermissions = errors.New("pq: Private key file has group or world access. Permissions should be u=rw (0600) or less.") + ErrCouldNotDetectUsername = errors.New("pq: Could not detect default username. Please provide one explicitly.") +) + +type drv struct{} + +func (d *drv) Open(name string) (driver.Conn, error) { + return Open(name) +} + +func init() { + sql.Register("postgres", &drv{}) +} + +type parameterStatus struct { + // server version in the same format as server_version_num, or 0 if + // unavailable + serverVersion int + + // the current location based on the TimeZone value of the session, if + // available + currentLocation *time.Location +} + +type transactionStatus byte + +const ( + txnStatusIdle transactionStatus = 'I' + txnStatusIdleInTransaction transactionStatus = 'T' + txnStatusInFailedTransaction transactionStatus = 'E' +) + +func (s transactionStatus) String() string { + switch s { + case txnStatusIdle: + return "idle" + case txnStatusIdleInTransaction: + return "idle in transaction" + case txnStatusInFailedTransaction: + return "in a failed transaction" + default: + errorf("unknown transactionStatus %d", s) + } + + panic("not reached") +} + +type Dialer interface { + Dial(network, address string) (net.Conn, error) + DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) +} + +type defaultDialer struct{} + +func (d defaultDialer) Dial(ntw, addr string) (net.Conn, error) { + return net.Dial(ntw, addr) +} +func (d defaultDialer) DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout(ntw, addr, timeout) +} + +type conn struct { + c net.Conn + buf *bufio.Reader + namei int + scratch [512]byte + txnStatus transactionStatus + + parameterStatus parameterStatus + + saveMessageType byte + saveMessageBuffer []byte + + // If true, this connection is bad and all public-facing functions should + // return ErrBadConn. + bad bool +} + +func (c *conn) writeBuf(b byte) *writeBuf { + c.scratch[0] = b + w := writeBuf(c.scratch[:5]) + return &w +} + +func Open(name string) (_ driver.Conn, err error) { + return DialOpen(defaultDialer{}, name) +} + +func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { + // Handle any panics during connection initialization. Note that we + // specifically do *not* want to use errRecover(), as that would turn any + // connection errors into ErrBadConns, hiding the real error message from + // the user. + defer errRecoverNoErrBadConn(&err) + + o := make(values) + + // A number of defaults are applied here, in this order: + // + // * Very low precedence defaults applied in every situation + // * Environment variables + // * Explicitly passed connection information + o.Set("host", "localhost") + o.Set("port", "5432") + // N.B.: Extra float digits should be set to 3, but that breaks + // Postgres 8.4 and older, where the max is 2. + o.Set("extra_float_digits", "2") + for k, v := range parseEnviron(os.Environ()) { + o.Set(k, v) + } + + if strings.HasPrefix(name, "postgres://") || strings.HasPrefix(name, "postgresql://") { + name, err = ParseURL(name) + if err != nil { + return nil, err + } + } + + if err := parseOpts(name, o); err != nil { + return nil, err + } + + // Use the "fallback" application name if necessary + if fallback := o.Get("fallback_application_name"); fallback != "" { + if !o.Isset("application_name") { + o.Set("application_name", fallback) + } + } + + // We can't work with any client_encoding other than UTF-8 currently. + // However, we have historically allowed the user to set it to UTF-8 + // explicitly, and there's no reason to break such programs, so allow that. + // Note that the "options" setting could also set client_encoding, but + // parsing its value is not worth it. Instead, we always explicitly send + // client_encoding as a separate run-time parameter, which should override + // anything set in options. + if enc := o.Get("client_encoding"); enc != "" && !isUTF8(enc) { + return nil, errors.New("client_encoding must be absent or 'UTF8'") + } + o.Set("client_encoding", "UTF8") + // DateStyle needs a similar treatment. + if datestyle := o.Get("datestyle"); datestyle != "" { + if datestyle != "ISO, MDY" { + panic(fmt.Sprintf("setting datestyle must be absent or %v; got %v", + "ISO, MDY", datestyle)) + } + } else { + o.Set("datestyle", "ISO, MDY") + } + + // If a user is not provided by any other means, the last + // resort is to use the current operating system provided user + // name. + if o.Get("user") == "" { + u, err := userCurrent() + if err != nil { + return nil, err + } else { + o.Set("user", u) + } + } + + c, err := dial(d, o) + if err != nil { + return nil, err + } + + cn := &conn{c: c} + cn.ssl(o) + cn.buf = bufio.NewReader(cn.c) + cn.startup(o) + // reset the deadline, in case one was set (see dial) + if timeout := o.Get("connect_timeout"); timeout != "" && timeout != "0" { + err = cn.c.SetDeadline(time.Time{}) + } + return cn, err +} + +func dial(d Dialer, o values) (net.Conn, error) { + ntw, addr := network(o) + // SSL is not necessary or supported over UNIX domain sockets + if ntw == "unix" { + o["sslmode"] = "disable" + } + + // Zero or not specified means wait indefinitely. + if timeout := o.Get("connect_timeout"); timeout != "" && timeout != "0" { + seconds, err := strconv.ParseInt(timeout, 10, 0) + if err != nil { + return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err) + } + duration := time.Duration(seconds) * time.Second + // connect_timeout should apply to the entire connection establishment + // procedure, so we both use a timeout for the TCP connection + // establishment and set a deadline for doing the initial handshake. + // The deadline is then reset after startup() is done. + deadline := time.Now().Add(duration) + conn, err := d.DialTimeout(ntw, addr, duration) + if err != nil { + return nil, err + } + err = conn.SetDeadline(deadline) + return conn, err + } + return d.Dial(ntw, addr) +} + +func network(o values) (string, string) { + host := o.Get("host") + + if strings.HasPrefix(host, "/") { + sockPath := path.Join(host, ".s.PGSQL."+o.Get("port")) + return "unix", sockPath + } + + return "tcp", host + ":" + o.Get("port") +} + +type values map[string]string + +func (vs values) Set(k, v string) { + vs[k] = v +} + +func (vs values) Get(k string) (v string) { + return vs[k] +} + +func (vs values) Isset(k string) bool { + _, ok := vs[k] + return ok +} + +// scanner implements a tokenizer for libpq-style option strings. +type scanner struct { + s []rune + i int +} + +// newScanner returns a new scanner initialized with the option string s. +func newScanner(s string) *scanner { + return &scanner{[]rune(s), 0} +} + +// Next returns the next rune. +// It returns 0, false if the end of the text has been reached. +func (s *scanner) Next() (rune, bool) { + if s.i >= len(s.s) { + return 0, false + } + r := s.s[s.i] + s.i++ + return r, true +} + +// SkipSpaces returns the next non-whitespace rune. +// It returns 0, false if the end of the text has been reached. +func (s *scanner) SkipSpaces() (rune, bool) { + r, ok := s.Next() + for unicode.IsSpace(r) && ok { + r, ok = s.Next() + } + return r, ok +} + +// parseOpts parses the options from name and adds them to the values. +// +// The parsing code is based on conninfo_parse from libpq's fe-connect.c +func parseOpts(name string, o values) error { + s := newScanner(name) + + for { + var ( + keyRunes, valRunes []rune + r rune + ok bool + ) + + if r, ok = s.SkipSpaces(); !ok { + break + } + + // Scan the key + for !unicode.IsSpace(r) && r != '=' { + keyRunes = append(keyRunes, r) + if r, ok = s.Next(); !ok { + break + } + } + + // Skip any whitespace if we're not at the = yet + if r != '=' { + r, ok = s.SkipSpaces() + } + + // The current character should be = + if r != '=' || !ok { + return fmt.Errorf(`missing "=" after %q in connection info string"`, string(keyRunes)) + } + + // Skip any whitespace after the = + if r, ok = s.SkipSpaces(); !ok { + // If we reach the end here, the last value is just an empty string as per libpq. + o.Set(string(keyRunes), "") + break + } + + if r != '\'' { + for !unicode.IsSpace(r) { + if r == '\\' { + if r, ok = s.Next(); !ok { + return fmt.Errorf(`missing character after backslash`) + } + } + valRunes = append(valRunes, r) + + if r, ok = s.Next(); !ok { + break + } + } + } else { + quote: + for { + if r, ok = s.Next(); !ok { + return fmt.Errorf(`unterminated quoted string literal in connection string`) + } + switch r { + case '\'': + break quote + case '\\': + r, _ = s.Next() + fallthrough + default: + valRunes = append(valRunes, r) + } + } + } + + o.Set(string(keyRunes), string(valRunes)) + } + + return nil +} + +func (cn *conn) isInTransaction() bool { + return cn.txnStatus == txnStatusIdleInTransaction || + cn.txnStatus == txnStatusInFailedTransaction +} + +func (cn *conn) checkIsInTransaction(intxn bool) { + if cn.isInTransaction() != intxn { + cn.bad = true + errorf("unexpected transaction status %v", cn.txnStatus) + } +} + +func (cn *conn) Begin() (_ driver.Tx, err error) { + if cn.bad { + return nil, driver.ErrBadConn + } + defer cn.errRecover(&err) + + cn.checkIsInTransaction(false) + _, commandTag, err := cn.simpleExec("BEGIN") + if err != nil { + return nil, err + } + if commandTag != "BEGIN" { + cn.bad = true + return nil, fmt.Errorf("unexpected command tag %s", commandTag) + } + if cn.txnStatus != txnStatusIdleInTransaction { + cn.bad = true + return nil, fmt.Errorf("unexpected transaction status %v", cn.txnStatus) + } + return cn, nil +} + +func (cn *conn) Commit() (err error) { + if cn.bad { + return driver.ErrBadConn + } + defer cn.errRecover(&err) + + cn.checkIsInTransaction(true) + // We don't want the client to think that everything is okay if it tries + // to commit a failed transaction. However, no matter what we return, + // database/sql will release this connection back into the free connection + // pool so we have to abort the current transaction here. Note that you + // would get the same behaviour if you issued a COMMIT in a failed + // transaction, so it's also the least surprising thing to do here. + if cn.txnStatus == txnStatusInFailedTransaction { + if err := cn.Rollback(); err != nil { + return err + } + return ErrInFailedTransaction + } + + _, commandTag, err := cn.simpleExec("COMMIT") + if err != nil { + if cn.isInTransaction() { + cn.bad = true + } + return err + } + if commandTag != "COMMIT" { + cn.bad = true + return fmt.Errorf("unexpected command tag %s", commandTag) + } + cn.checkIsInTransaction(false) + return nil +} + +func (cn *conn) Rollback() (err error) { + if cn.bad { + return driver.ErrBadConn + } + defer cn.errRecover(&err) + + cn.checkIsInTransaction(true) + _, commandTag, err := cn.simpleExec("ROLLBACK") + if err != nil { + if cn.isInTransaction() { + cn.bad = true + } + return err + } + if commandTag != "ROLLBACK" { + return fmt.Errorf("unexpected command tag %s", commandTag) + } + cn.checkIsInTransaction(false) + return nil +} + +func (cn *conn) gname() string { + cn.namei++ + return strconv.FormatInt(int64(cn.namei), 10) +} + +func (cn *conn) simpleExec(q string) (res driver.Result, commandTag string, err error) { + b := cn.writeBuf('Q') + b.string(q) + cn.send(b) + + for { + t, r := cn.recv1() + switch t { + case 'C': + res, commandTag = cn.parseComplete(r.string()) + case 'Z': + cn.processReadyForQuery(r) + // done + return + case 'E': + err = parseError(r) + case 'T', 'D', 'I': + // ignore any results + default: + cn.bad = true + errorf("unknown response for simple query: %q", t) + } + } +} + +func (cn *conn) simpleQuery(q string) (res driver.Rows, err error) { + defer cn.errRecover(&err) + + st := &stmt{cn: cn, name: ""} + + b := cn.writeBuf('Q') + b.string(q) + cn.send(b) + + for { + t, r := cn.recv1() + switch t { + case 'C', 'I': + // We allow queries which don't return any results through Query as + // well as Exec. We still have to give database/sql a rows object + // the user can close, though, to avoid connections from being + // leaked. A "rows" with done=true works fine for that purpose. + if err != nil { + cn.bad = true + errorf("unexpected message %q in simple query execution", t) + } + res = &rows{st: st, done: true} + case 'Z': + cn.processReadyForQuery(r) + // done + return + case 'E': + res = nil + err = parseError(r) + case 'D': + if res == nil { + cn.bad = true + errorf("unexpected DataRow in simple query execution") + } + // the query didn't fail; kick off to Next + cn.saveMessage(t, r) + return + case 'T': + // res might be non-nil here if we received a previous + // CommandComplete, but that's fine; just overwrite it + res = &rows{st: st} + st.cols, st.rowTyps = parseMeta(r) + + // To work around a bug in QueryRow in Go 1.2 and earlier, wait + // until the first DataRow has been received. + default: + cn.bad = true + errorf("unknown response for simple query: %q", t) + } + } +} + +func (cn *conn) prepareTo(q, stmtName string) (_ *stmt, err error) { + st := &stmt{cn: cn, name: stmtName} + + b := cn.writeBuf('P') + b.string(st.name) + b.string(q) + b.int16(0) + cn.send(b) + + b = cn.writeBuf('D') + b.byte('S') + b.string(st.name) + cn.send(b) + + cn.send(cn.writeBuf('S')) + + for { + t, r := cn.recv1() + switch t { + case '1': + case 't': + nparams := r.int16() + st.paramTyps = make([]oid.Oid, nparams) + + for i := range st.paramTyps { + st.paramTyps[i] = r.oid() + } + case 'T': + st.cols, st.rowTyps = parseMeta(r) + case 'n': + // no data + case 'Z': + cn.processReadyForQuery(r) + return st, err + case 'E': + err = parseError(r) + default: + cn.bad = true + errorf("unexpected describe rows response: %q", t) + } + } +} + +func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) { + if cn.bad { + return nil, driver.ErrBadConn + } + defer cn.errRecover(&err) + + if len(q) >= 4 && strings.EqualFold(q[:4], "COPY") { + return cn.prepareCopyIn(q) + } + return cn.prepareTo(q, cn.gname()) +} + +func (cn *conn) Close() (err error) { + if cn.bad { + return driver.ErrBadConn + } + defer cn.errRecover(&err) + + // Don't go through send(); ListenerConn relies on us not scribbling on the + // scratch buffer of this connection. + err = cn.sendSimpleMessage('X') + if err != nil { + return err + } + + return cn.c.Close() +} + +// Implement the "Queryer" interface +func (cn *conn) Query(query string, args []driver.Value) (_ driver.Rows, err error) { + if cn.bad { + return nil, driver.ErrBadConn + } + defer cn.errRecover(&err) + + // Check to see if we can use the "simpleQuery" interface, which is + // *much* faster than going through prepare/exec + if len(args) == 0 { + return cn.simpleQuery(query) + } + + st, err := cn.prepareTo(query, "") + if err != nil { + panic(err) + } + + st.exec(args) + return &rows{st: st}, nil +} + +// Implement the optional "Execer" interface for one-shot queries +func (cn *conn) Exec(query string, args []driver.Value) (_ driver.Result, err error) { + if cn.bad { + return nil, driver.ErrBadConn + } + defer cn.errRecover(&err) + + // Check to see if we can use the "simpleExec" interface, which is + // *much* faster than going through prepare/exec + if len(args) == 0 { + // ignore commandTag, our caller doesn't care + r, _, err := cn.simpleExec(query) + return r, err + } + + // Use the unnamed statement to defer planning until bind + // time, or else value-based selectivity estimates cannot be + // used. + st, err := cn.prepareTo(query, "") + if err != nil { + panic(err) + } + + r, err := st.Exec(args) + if err != nil { + panic(err) + } + + return r, err +} + +// Assumes len(*m) is > 5 +func (cn *conn) send(m *writeBuf) { + b := (*m)[1:] + binary.BigEndian.PutUint32(b, uint32(len(b))) + + if (*m)[0] == 0 { + *m = b + } + + _, err := cn.c.Write(*m) + if err != nil { + panic(err) + } +} + +// Send a message of type typ to the server on the other end of cn. The +// message should have no payload. This method does not use the scratch +// buffer. +func (cn *conn) sendSimpleMessage(typ byte) (err error) { + _, err = cn.c.Write([]byte{typ, '\x00', '\x00', '\x00', '\x04'}) + return err +} + +// saveMessage memorizes a message and its buffer in the conn struct. +// recvMessage will then return these values on the next call to it. This +// method is useful in cases where you have to see what the next message is +// going to be (e.g. to see whether it's an error or not) but you can't handle +// the message yourself. +func (cn *conn) saveMessage(typ byte, buf *readBuf) { + if cn.saveMessageType != 0 { + cn.bad = true + errorf("unexpected saveMessageType %d", cn.saveMessageType) + } + cn.saveMessageType = typ + cn.saveMessageBuffer = *buf +} + +// recvMessage receives any message from the backend, or returns an error if +// a problem occurred while reading the message. +func (cn *conn) recvMessage(r *readBuf) (byte, error) { + // workaround for a QueryRow bug, see exec + if cn.saveMessageType != 0 { + t := cn.saveMessageType + *r = cn.saveMessageBuffer + cn.saveMessageType = 0 + cn.saveMessageBuffer = nil + return t, nil + } + + x := cn.scratch[:5] + _, err := io.ReadFull(cn.buf, x) + if err != nil { + return 0, err + } + + // read the type and length of the message that follows + t := x[0] + n := int(binary.BigEndian.Uint32(x[1:])) - 4 + var y []byte + if n <= len(cn.scratch) { + y = cn.scratch[:n] + } else { + y = make([]byte, n) + } + _, err = io.ReadFull(cn.buf, y) + if err != nil { + return 0, err + } + *r = y + return t, nil +} + +// recv receives a message from the backend, but if an error happened while +// reading the message or the received message was an ErrorResponse, it panics. +// NoticeResponses are ignored. This function should generally be used only +// during the startup sequence. +func (cn *conn) recv() (t byte, r *readBuf) { + for { + var err error + r = &readBuf{} + t, err = cn.recvMessage(r) + if err != nil { + panic(err) + } + + switch t { + case 'E': + panic(parseError(r)) + case 'N': + // ignore + default: + return + } + } +} + +// recv1Buf is exactly equivalent to recv1, except it uses a buffer supplied by +// the caller to avoid an allocation. +func (cn *conn) recv1Buf(r *readBuf) byte { + for { + t, err := cn.recvMessage(r) + if err != nil { + panic(err) + } + + switch t { + case 'A', 'N': + // ignore + case 'S': + cn.processParameterStatus(r) + default: + return t + } + } +} + +// recv1 receives a message from the backend, panicking if an error occurs +// while attempting to read it. All asynchronous messages are ignored, with +// the exception of ErrorResponse. +func (cn *conn) recv1() (t byte, r *readBuf) { + r = &readBuf{} + t = cn.recv1Buf(r) + return t, r +} + +func (cn *conn) ssl(o values) { + verifyCaOnly := false + tlsConf := tls.Config{} + switch mode := o.Get("sslmode"); mode { + case "require", "": + tlsConf.InsecureSkipVerify = true + case "verify-ca": + // We must skip TLS's own verification since it requires full + // verification since Go 1.3. + tlsConf.InsecureSkipVerify = true + verifyCaOnly = true + case "verify-full": + tlsConf.ServerName = o.Get("host") + case "disable": + return + default: + errorf(`unsupported sslmode %q; only "require" (default), "verify-full", and "disable" supported`, mode) + } + + cn.setupSSLClientCertificates(&tlsConf, o) + cn.setupSSLCA(&tlsConf, o) + + w := cn.writeBuf(0) + w.int32(80877103) + cn.send(w) + + b := cn.scratch[:1] + _, err := io.ReadFull(cn.c, b) + if err != nil { + panic(err) + } + + if b[0] != 'S' { + panic(ErrSSLNotSupported) + } + + client := tls.Client(cn.c, &tlsConf) + if verifyCaOnly { + cn.verifyCA(client, &tlsConf) + } + cn.c = client +} + +// verifyCA carries out a TLS handshake to the server and verifies the +// presented certificate against the effective CA, i.e. the one specified in +// sslrootcert or the system CA if sslrootcert was not specified. +func (cn *conn) verifyCA(client *tls.Conn, tlsConf *tls.Config) { + err := client.Handshake() + if err != nil { + panic(err) + } + certs := client.ConnectionState().PeerCertificates + opts := x509.VerifyOptions{ + DNSName: client.ConnectionState().ServerName, + Intermediates: x509.NewCertPool(), + Roots: tlsConf.RootCAs, + } + for i, cert := range certs { + if i == 0 { + continue + } + opts.Intermediates.AddCert(cert) + } + _, err = certs[0].Verify(opts) + if err != nil { + panic(err) + } +} + +// This function sets up SSL client certificates based on either the "sslkey" +// and "sslcert" settings (possibly set via the environment variables PGSSLKEY +// and PGSSLCERT, respectively), or if they aren't set, from the .postgresql +// directory in the user's home directory. If the file paths are set +// explicitly, the files must exist. The key file must also not be +// world-readable, or this function will panic with +// ErrSSLKeyHasWorldPermissions. +func (cn *conn) setupSSLClientCertificates(tlsConf *tls.Config, o values) { + var missingOk bool + + sslkey := o.Get("sslkey") + sslcert := o.Get("sslcert") + if sslkey != "" && sslcert != "" { + // If the user has set an sslkey and sslcert, they *must* exist. + missingOk = false + } else { + // Automatically load certificates from ~/.postgresql. + user, err := user.Current() + if err != nil { + // user.Current() might fail when cross-compiling. We have to + // ignore the error and continue without client certificates, since + // we wouldn't know where to load them from. + return + } + + sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key") + sslcert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt") + missingOk = true + } + + // Check that both files exist, and report the error or stop, depending on + // which behaviour we want. Note that we don't do any more extensive + // checks than this (such as checking that the paths aren't directories); + // LoadX509KeyPair() will take care of the rest. + keyfinfo, err := os.Stat(sslkey) + if err != nil && missingOk { + return + } else if err != nil { + panic(err) + } + _, err = os.Stat(sslcert) + if err != nil && missingOk { + return + } else if err != nil { + panic(err) + } + + // If we got this far, the key file must also have the correct permissions + kmode := keyfinfo.Mode() + if kmode != kmode&0600 { + panic(ErrSSLKeyHasWorldPermissions) + } + + cert, err := tls.LoadX509KeyPair(sslcert, sslkey) + if err != nil { + panic(err) + } + tlsConf.Certificates = []tls.Certificate{cert} +} + +// Sets up RootCAs in the TLS configuration if sslrootcert is set. +func (cn *conn) setupSSLCA(tlsConf *tls.Config, o values) { + if sslrootcert := o.Get("sslrootcert"); sslrootcert != "" { + tlsConf.RootCAs = x509.NewCertPool() + + cert, err := ioutil.ReadFile(sslrootcert) + if err != nil { + panic(err) + } + + ok := tlsConf.RootCAs.AppendCertsFromPEM(cert) + if !ok { + errorf("couldn't parse pem in sslrootcert") + } + } +} + +// isDriverSetting returns true iff a setting is purely for configuring the +// driver's options and should not be sent to the server in the connection +// startup packet. +func isDriverSetting(key string) bool { + switch key { + case "host", "port": + return true + case "password": + return true + case "sslmode", "sslcert", "sslkey", "sslrootcert": + return true + case "fallback_application_name": + return true + case "connect_timeout": + return true + + default: + return false + } +} + +func (cn *conn) startup(o values) { + w := cn.writeBuf(0) + w.int32(196608) + // Send the backend the name of the database we want to connect to, and the + // user we want to connect as. Additionally, we send over any run-time + // parameters potentially included in the connection string. If the server + // doesn't recognize any of them, it will reply with an error. + for k, v := range o { + if isDriverSetting(k) { + // skip options which can't be run-time parameters + continue + } + // The protocol requires us to supply the database name as "database" + // instead of "dbname". + if k == "dbname" { + k = "database" + } + w.string(k) + w.string(v) + } + w.string("") + cn.send(w) + + for { + t, r := cn.recv() + switch t { + case 'K': + case 'S': + cn.processParameterStatus(r) + case 'R': + cn.auth(r, o) + case 'Z': + cn.processReadyForQuery(r) + return + default: + errorf("unknown response for startup: %q", t) + } + } +} + +func (cn *conn) auth(r *readBuf, o values) { + switch code := r.int32(); code { + case 0: + // OK + case 3: + w := cn.writeBuf('p') + w.string(o.Get("password")) + cn.send(w) + + t, r := cn.recv() + if t != 'R' { + errorf("unexpected password response: %q", t) + } + + if r.int32() != 0 { + errorf("unexpected authentication response: %q", t) + } + case 5: + s := string(r.next(4)) + w := cn.writeBuf('p') + w.string("md5" + md5s(md5s(o.Get("password")+o.Get("user"))+s)) + cn.send(w) + + t, r := cn.recv() + if t != 'R' { + errorf("unexpected password response: %q", t) + } + + if r.int32() != 0 { + errorf("unexpected authentication response: %q", t) + } + default: + errorf("unknown authentication response: %d", code) + } +} + +type stmt struct { + cn *conn + name string + cols []string + rowTyps []oid.Oid + paramTyps []oid.Oid + closed bool +} + +func (st *stmt) Close() (err error) { + if st.closed { + return nil + } + if st.cn.bad { + return driver.ErrBadConn + } + defer st.cn.errRecover(&err) + + w := st.cn.writeBuf('C') + w.byte('S') + w.string(st.name) + st.cn.send(w) + + st.cn.send(st.cn.writeBuf('S')) + + t, _ := st.cn.recv1() + if t != '3' { + st.cn.bad = true + errorf("unexpected close response: %q", t) + } + st.closed = true + + t, r := st.cn.recv1() + if t != 'Z' { + st.cn.bad = true + errorf("expected ready for query, but got: %q", t) + } + st.cn.processReadyForQuery(r) + + return nil +} + +func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) { + if st.cn.bad { + return nil, driver.ErrBadConn + } + defer st.cn.errRecover(&err) + + st.exec(v) + return &rows{st: st}, nil +} + +func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) { + if st.cn.bad { + return nil, driver.ErrBadConn + } + defer st.cn.errRecover(&err) + + st.exec(v) + + for { + t, r := st.cn.recv1() + switch t { + case 'E': + err = parseError(r) + case 'C': + res, _ = st.cn.parseComplete(r.string()) + case 'Z': + st.cn.processReadyForQuery(r) + // done + return + case 'T', 'D', 'I': + // ignore any results + default: + st.cn.bad = true + errorf("unknown exec response: %q", t) + } + } +} + +func (st *stmt) exec(v []driver.Value) { + if len(v) >= 65536 { + errorf("got %d parameters but PostgreSQL only supports 65535 parameters", len(v)) + } + if len(v) != len(st.paramTyps) { + errorf("got %d parameters but the statement requires %d", len(v), len(st.paramTyps)) + } + + w := st.cn.writeBuf('B') + w.string("") + w.string(st.name) + w.int16(0) + w.int16(len(v)) + for i, x := range v { + if x == nil { + w.int32(-1) + } else { + b := encode(&st.cn.parameterStatus, x, st.paramTyps[i]) + w.int32(len(b)) + w.bytes(b) + } + } + w.int16(0) + st.cn.send(w) + + w = st.cn.writeBuf('E') + w.string("") + w.int32(0) + st.cn.send(w) + + st.cn.send(st.cn.writeBuf('S')) + + var err error + for { + t, r := st.cn.recv1() + switch t { + case 'E': + err = parseError(r) + case '2': + if err != nil { + panic(err) + } + goto workaround + case 'Z': + st.cn.processReadyForQuery(r) + if err != nil { + panic(err) + } + return + default: + st.cn.bad = true + errorf("unexpected bind response: %q", t) + } + } + + // Work around a bug in sql.DB.QueryRow: in Go 1.2 and earlier it ignores + // any errors from rows.Next, which masks errors that happened during the + // execution of the query. To avoid the problem in common cases, we wait + // here for one more message from the database. If it's not an error the + // query will likely succeed (or perhaps has already, if it's a + // CommandComplete), so we push the message into the conn struct; recv1 + // will return it as the next message for rows.Next or rows.Close. + // However, if it's an error, we wait until ReadyForQuery and then return + // the error to our caller. +workaround: + for { + t, r := st.cn.recv1() + switch t { + case 'E': + err = parseError(r) + case 'C', 'D', 'I': + // the query didn't fail, but we can't process this message + st.cn.saveMessage(t, r) + return + case 'Z': + if err == nil { + st.cn.bad = true + errorf("unexpected ReadyForQuery during extended query execution") + } + st.cn.processReadyForQuery(r) + panic(err) + default: + st.cn.bad = true + errorf("unexpected message during query execution: %q", t) + } + } +} + +func (st *stmt) NumInput() int { + return len(st.paramTyps) +} + +// parseComplete parses the "command tag" from a CommandComplete message, and +// returns the number of rows affected (if applicable) and a string +// identifying only the command that was executed, e.g. "ALTER TABLE". If the +// command tag could not be parsed, parseComplete panics. +func (cn *conn) parseComplete(commandTag string) (driver.Result, string) { + commandsWithAffectedRows := []string{ + "SELECT ", + // INSERT is handled below + "UPDATE ", + "DELETE ", + "FETCH ", + "MOVE ", + "COPY ", + } + + var affectedRows *string + for _, tag := range commandsWithAffectedRows { + if strings.HasPrefix(commandTag, tag) { + t := commandTag[len(tag):] + affectedRows = &t + commandTag = tag[:len(tag)-1] + break + } + } + // INSERT also includes the oid of the inserted row in its command tag. + // Oids in user tables are deprecated, and the oid is only returned when + // exactly one row is inserted, so it's unlikely to be of value to any + // real-world application and we can ignore it. + if affectedRows == nil && strings.HasPrefix(commandTag, "INSERT ") { + parts := strings.Split(commandTag, " ") + if len(parts) != 3 { + cn.bad = true + errorf("unexpected INSERT command tag %s", commandTag) + } + affectedRows = &parts[len(parts)-1] + commandTag = "INSERT" + } + // There should be no affected rows attached to the tag, just return it + if affectedRows == nil { + return driver.RowsAffected(0), commandTag + } + n, err := strconv.ParseInt(*affectedRows, 10, 64) + if err != nil { + cn.bad = true + errorf("could not parse commandTag: %s", err) + } + return driver.RowsAffected(n), commandTag +} + +type rows struct { + st *stmt + done bool + rb readBuf +} + +func (rs *rows) Close() error { + // no need to look at cn.bad as Next() will + for { + err := rs.Next(nil) + switch err { + case nil: + case io.EOF: + return nil + default: + return err + } + } +} + +func (rs *rows) Columns() []string { + return rs.st.cols +} + +func (rs *rows) Next(dest []driver.Value) (err error) { + if rs.done { + return io.EOF + } + + conn := rs.st.cn + if conn.bad { + return driver.ErrBadConn + } + defer conn.errRecover(&err) + + for { + t := conn.recv1Buf(&rs.rb) + switch t { + case 'E': + err = parseError(&rs.rb) + case 'C', 'I': + continue + case 'Z': + conn.processReadyForQuery(&rs.rb) + rs.done = true + if err != nil { + return err + } + return io.EOF + case 'D': + n := rs.rb.int16() + if n < len(dest) { + dest = dest[:n] + } + for i := range dest { + l := rs.rb.int32() + if l == -1 { + dest[i] = nil + continue + } + dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.st.rowTyps[i]) + } + return + default: + errorf("unexpected message after execute: %q", t) + } + } +} + +// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be +// used as part of an SQL statement. For example: +// +// tblname := "my_table" +// data := "my_data" +// err = db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", pq.QuoteIdentifier(tblname)), data) +// +// Any double quotes in name will be escaped. The quoted identifier will be +// case sensitive when used in a query. If the input string contains a zero +// byte, the result will be truncated immediately before it. +func QuoteIdentifier(name string) string { + end := strings.IndexRune(name, 0) + if end > -1 { + name = name[:end] + } + return `"` + strings.Replace(name, `"`, `""`, -1) + `"` +} + +func md5s(s string) string { + h := md5.New() + h.Write([]byte(s)) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +func (c *conn) processParameterStatus(r *readBuf) { + var err error + + param := r.string() + switch param { + case "server_version": + var major1 int + var major2 int + var minor int + _, err = fmt.Sscanf(r.string(), "%d.%d.%d", &major1, &major2, &minor) + if err == nil { + c.parameterStatus.serverVersion = major1*10000 + major2*100 + minor + } + + case "TimeZone": + c.parameterStatus.currentLocation, err = time.LoadLocation(r.string()) + if err != nil { + c.parameterStatus.currentLocation = nil + } + + default: + // ignore + } +} + +func (c *conn) processReadyForQuery(r *readBuf) { + c.txnStatus = transactionStatus(r.byte()) +} + +func parseMeta(r *readBuf) (cols []string, rowTyps []oid.Oid) { + n := r.int16() + cols = make([]string, n) + rowTyps = make([]oid.Oid, n) + for i := range cols { + cols[i] = r.string() + r.next(6) + rowTyps[i] = r.oid() + r.next(8) + } + return +} + +// parseEnviron tries to mimic some of libpq's environment handling +// +// To ease testing, it does not directly reference os.Environ, but is +// designed to accept its output. +// +// Environment-set connection information is intended to have a higher +// precedence than a library default but lower than any explicitly +// passed information (such as in the URL or connection string). +func parseEnviron(env []string) (out map[string]string) { + out = make(map[string]string) + + for _, v := range env { + parts := strings.SplitN(v, "=", 2) + + accrue := func(keyname string) { + out[keyname] = parts[1] + } + unsupported := func() { + panic(fmt.Sprintf("setting %v not supported", parts[0])) + } + + // The order of these is the same as is seen in the + // PostgreSQL 9.1 manual. Unsupported but well-defined + // keys cause a panic; these should be unset prior to + // execution. Options which pq expects to be set to a + // certain value are allowed, but must be set to that + // value if present (they can, of course, be absent). + switch parts[0] { + case "PGHOST": + accrue("host") + case "PGHOSTADDR": + unsupported() + case "PGPORT": + accrue("port") + case "PGDATABASE": + accrue("dbname") + case "PGUSER": + accrue("user") + case "PGPASSWORD": + accrue("password") + case "PGPASSFILE", "PGSERVICE", "PGSERVICEFILE", "PGREALM": + unsupported() + case "PGOPTIONS": + accrue("options") + case "PGAPPNAME": + accrue("application_name") + case "PGSSLMODE": + accrue("sslmode") + case "PGSSLCERT": + accrue("sslcert") + case "PGSSLKEY": + accrue("sslkey") + case "PGSSLROOTCERT": + accrue("sslrootcert") + case "PGREQUIRESSL", "PGSSLCRL": + unsupported() + case "PGREQUIREPEER": + unsupported() + case "PGKRBSRVNAME", "PGGSSLIB": + unsupported() + case "PGCONNECT_TIMEOUT": + accrue("connect_timeout") + case "PGCLIENTENCODING": + accrue("client_encoding") + case "PGDATESTYLE": + accrue("datestyle") + case "PGTZ": + accrue("timezone") + case "PGGEQO": + accrue("geqo") + case "PGSYSCONFDIR", "PGLOCALEDIR": + unsupported() + } + } + + return out +} + +// isUTF8 returns whether name is a fuzzy variation of the string "UTF-8". +func isUTF8(name string) bool { + // Recognize all sorts of silly things as "UTF-8", like Postgres does + s := strings.Map(alnumLowerASCII, name) + return s == "utf8" || s == "unicode" +} + +func alnumLowerASCII(ch rune) rune { + if 'A' <= ch && ch <= 'Z' { + return ch + ('a' - 'A') + } + if 'a' <= ch && ch <= 'z' || '0' <= ch && ch <= '9' { + return ch + } + return -1 // discard +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/conn_test.go b/Godeps/_workspace/src/github.com/lib/pq/conn_test.go new file mode 100644 index 000000000..741fd761a --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/conn_test.go @@ -0,0 +1,1282 @@ +package pq + +import ( + "database/sql" + "database/sql/driver" + "fmt" + "io" + "os" + "reflect" + "testing" + "time" +) + +type Fatalistic interface { + Fatal(args ...interface{}) +} + +func openTestConnConninfo(conninfo string) (*sql.DB, error) { + defaultTo := func(envvar string, value string) { + if os.Getenv(envvar) == "" { + os.Setenv(envvar, value) + } + } + defaultTo("PGDATABASE", "pqgotest") + defaultTo("PGSSLMODE", "disable") + defaultTo("PGCONNECT_TIMEOUT", "20") + return sql.Open("postgres", conninfo) +} + +func openTestConn(t Fatalistic) *sql.DB { + conn, err := openTestConnConninfo("") + if err != nil { + t.Fatal(err) + } + + return conn +} + +func getServerVersion(t *testing.T, db *sql.DB) int { + var version int + err := db.QueryRow("SHOW server_version_num").Scan(&version) + if err != nil { + t.Fatal(err) + } + return version +} + +func TestReconnect(t *testing.T) { + db1 := openTestConn(t) + defer db1.Close() + tx, err := db1.Begin() + if err != nil { + t.Fatal(err) + } + var pid1 int + err = tx.QueryRow("SELECT pg_backend_pid()").Scan(&pid1) + if err != nil { + t.Fatal(err) + } + db2 := openTestConn(t) + defer db2.Close() + _, err = db2.Exec("SELECT pg_terminate_backend($1)", pid1) + if err != nil { + t.Fatal(err) + } + // The rollback will probably "fail" because we just killed + // its connection above + _ = tx.Rollback() + + const expected int = 42 + var result int + err = db1.QueryRow(fmt.Sprintf("SELECT %d", expected)).Scan(&result) + if err != nil { + t.Fatal(err) + } + if result != expected { + t.Errorf("got %v; expected %v", result, expected) + } +} + +func TestCommitInFailedTransaction(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + rows, err := txn.Query("SELECT error") + if err == nil { + rows.Close() + t.Fatal("expected failure") + } + err = txn.Commit() + if err != ErrInFailedTransaction { + t.Fatalf("expected ErrInFailedTransaction; got %#v", err) + } +} + +func TestOpenURL(t *testing.T) { + testURL := func(url string) { + db, err := openTestConnConninfo(url) + if err != nil { + t.Fatal(err) + } + defer db.Close() + // database/sql might not call our Open at all unless we do something with + // the connection + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + txn.Rollback() + } + testURL("postgres://") + testURL("postgresql://") +} + +func TestExec(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("CREATE TEMP TABLE temp (a int)") + if err != nil { + t.Fatal(err) + } + + r, err := db.Exec("INSERT INTO temp VALUES (1)") + if err != nil { + t.Fatal(err) + } + + if n, _ := r.RowsAffected(); n != 1 { + t.Fatalf("expected 1 row affected, not %d", n) + } + + r, err = db.Exec("INSERT INTO temp VALUES ($1), ($2), ($3)", 1, 2, 3) + if err != nil { + t.Fatal(err) + } + + if n, _ := r.RowsAffected(); n != 3 { + t.Fatalf("expected 3 rows affected, not %d", n) + } + + // SELECT doesn't send the number of returned rows in the command tag + // before 9.0 + if getServerVersion(t, db) >= 90000 { + r, err = db.Exec("SELECT g FROM generate_series(1, 2) g") + if err != nil { + t.Fatal(err) + } + if n, _ := r.RowsAffected(); n != 2 { + t.Fatalf("expected 2 rows affected, not %d", n) + } + + r, err = db.Exec("SELECT g FROM generate_series(1, $1) g", 3) + if err != nil { + t.Fatal(err) + } + if n, _ := r.RowsAffected(); n != 3 { + t.Fatalf("expected 3 rows affected, not %d", n) + } + } +} + +func TestStatment(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + st, err := db.Prepare("SELECT 1") + if err != nil { + t.Fatal(err) + } + + st1, err := db.Prepare("SELECT 2") + if err != nil { + t.Fatal(err) + } + + r, err := st.Query() + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if !r.Next() { + t.Fatal("expected row") + } + + var i int + err = r.Scan(&i) + if err != nil { + t.Fatal(err) + } + + if i != 1 { + t.Fatalf("expected 1, got %d", i) + } + + // st1 + + r1, err := st1.Query() + if err != nil { + t.Fatal(err) + } + defer r1.Close() + + if !r1.Next() { + if r.Err() != nil { + t.Fatal(r1.Err()) + } + t.Fatal("expected row") + } + + err = r1.Scan(&i) + if err != nil { + t.Fatal(err) + } + + if i != 2 { + t.Fatalf("expected 2, got %d", i) + } +} + +func TestRowsCloseBeforeDone(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + r, err := db.Query("SELECT 1") + if err != nil { + t.Fatal(err) + } + + err = r.Close() + if err != nil { + t.Fatal(err) + } + + if r.Next() { + t.Fatal("unexpected row") + } + + if r.Err() != nil { + t.Fatal(r.Err()) + } +} + +func TestParameterCountMismatch(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + var notused int + err := db.QueryRow("SELECT false", 1).Scan(¬used) + if err == nil { + t.Fatal("expected err") + } + // make sure we clean up correctly + err = db.QueryRow("SELECT 1").Scan(¬used) + if err != nil { + t.Fatal(err) + } + + err = db.QueryRow("SELECT $1").Scan(¬used) + if err == nil { + t.Fatal("expected err") + } + // make sure we clean up correctly + err = db.QueryRow("SELECT 1").Scan(¬used) + if err != nil { + t.Fatal(err) + } +} + +// Test that EmptyQueryResponses are handled correctly. +func TestEmptyQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("") + if err != nil { + t.Fatal(err) + } + rows, err := db.Query("") + if err != nil { + t.Fatal(err) + } + cols, err := rows.Columns() + if err != nil { + t.Fatal(err) + } + if len(cols) != 0 { + t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) + } + if rows.Next() { + t.Fatal("unexpected row") + } + if rows.Err() != nil { + t.Fatal(rows.Err()) + } + + stmt, err := db.Prepare("") + if err != nil { + t.Fatal(err) + } + _, err = stmt.Exec() + if err != nil { + t.Fatal(err) + } + rows, err = stmt.Query() + if err != nil { + t.Fatal(err) + } + cols, err = rows.Columns() + if err != nil { + t.Fatal(err) + } + if len(cols) != 0 { + t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) + } + if rows.Next() { + t.Fatal("unexpected row") + } + if rows.Err() != nil { + t.Fatal(rows.Err()) + } +} + +func TestEncodeDecode(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + q := ` + SELECT + E'\\000\\001\\002'::bytea, + 'foobar'::text, + NULL::integer, + '2000-1-1 01:02:03.04-7'::timestamptz, + 0::boolean, + 123, + 3.14::float8 + WHERE + E'\\000\\001\\002'::bytea = $1 + AND 'foobar'::text = $2 + AND $3::integer is NULL + ` + // AND '2000-1-1 12:00:00.000000-7'::timestamp = $3 + + exp1 := []byte{0, 1, 2} + exp2 := "foobar" + + r, err := db.Query(q, exp1, exp2, nil) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if !r.Next() { + if r.Err() != nil { + t.Fatal(r.Err()) + } + t.Fatal("expected row") + } + + var got1 []byte + var got2 string + var got3 = sql.NullInt64{Valid: true} + var got4 time.Time + var got5, got6, got7 interface{} + + err = r.Scan(&got1, &got2, &got3, &got4, &got5, &got6, &got7) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(exp1, got1) { + t.Errorf("expected %q byte: %q", exp1, got1) + } + + if !reflect.DeepEqual(exp2, got2) { + t.Errorf("expected %q byte: %q", exp2, got2) + } + + if got3.Valid { + t.Fatal("expected invalid") + } + + if got4.Year() != 2000 { + t.Fatal("wrong year") + } + + if got5 != false { + t.Fatalf("expected false, got %q", got5) + } + + if got6 != int64(123) { + t.Fatalf("expected 123, got %d", got6) + } + + if got7 != float64(3.14) { + t.Fatalf("expected 3.14, got %f", got7) + } +} + +func TestNoData(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + st, err := db.Prepare("SELECT 1 WHERE true = false") + if err != nil { + t.Fatal(err) + } + defer st.Close() + + r, err := st.Query() + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if r.Next() { + if r.Err() != nil { + t.Fatal(r.Err()) + } + t.Fatal("unexpected row") + } + + _, err = db.Query("SELECT * FROM nonexistenttable WHERE age=$1", 20) + if err == nil { + t.Fatal("Should have raised an error on non existent table") + } + + _, err = db.Query("SELECT * FROM nonexistenttable") + if err == nil { + t.Fatal("Should have raised an error on non existent table") + } +} + +func TestErrorDuringStartup(t *testing.T) { + // Don't use the normal connection setup, this is intended to + // blow up in the startup packet from a non-existent user. + db, err := openTestConnConninfo("user=thisuserreallydoesntexist") + if err != nil { + t.Fatal(err) + } + defer db.Close() + + _, err = db.Begin() + if err == nil { + t.Fatal("expected error") + } + + e, ok := err.(*Error) + if !ok { + t.Fatalf("expected Error, got %#v", err) + } else if e.Code.Name() != "invalid_authorization_specification" && e.Code.Name() != "invalid_password" { + t.Fatalf("expected invalid_authorization_specification or invalid_password, got %s (%+v)", e.Code.Name(), err) + } +} + +func TestBadConn(t *testing.T) { + var err error + + cn := conn{} + func() { + defer cn.errRecover(&err) + panic(io.EOF) + }() + if err != driver.ErrBadConn { + t.Fatalf("expected driver.ErrBadConn, got: %#v", err) + } + if !cn.bad { + t.Fatalf("expected cn.bad") + } + + cn = conn{} + func() { + defer cn.errRecover(&err) + e := &Error{Severity: Efatal} + panic(e) + }() + if err != driver.ErrBadConn { + t.Fatalf("expected driver.ErrBadConn, got: %#v", err) + } + if !cn.bad { + t.Fatalf("expected cn.bad") + } +} + +func TestErrorOnExec(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)") + if err != nil { + t.Fatal(err) + } + + _, err = txn.Exec("INSERT INTO foo VALUES (0), (0)") + if err == nil { + t.Fatal("Should have raised error") + } + + e, ok := err.(*Error) + if !ok { + t.Fatalf("expected Error, got %#v", err) + } else if e.Code.Name() != "unique_violation" { + t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err) + } +} + +func TestErrorOnQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)") + if err != nil { + t.Fatal(err) + } + + _, err = txn.Query("INSERT INTO foo VALUES (0), (0)") + if err == nil { + t.Fatal("Should have raised error") + } + + e, ok := err.(*Error) + if !ok { + t.Fatalf("expected Error, got %#v", err) + } else if e.Code.Name() != "unique_violation" { + t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err) + } +} + +func TestErrorOnQueryRowSimpleQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)") + if err != nil { + t.Fatal(err) + } + + var v int + err = txn.QueryRow("INSERT INTO foo VALUES (0), (0)").Scan(&v) + if err == nil { + t.Fatal("Should have raised error") + } + + e, ok := err.(*Error) + if !ok { + t.Fatalf("expected Error, got %#v", err) + } else if e.Code.Name() != "unique_violation" { + t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err) + } +} + +// Test the QueryRow bug workarounds in stmt.exec() and simpleQuery() +func TestQueryRowBugWorkaround(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + // stmt.exec() + _, err := db.Exec("CREATE TEMP TABLE notnulltemp (a varchar(10) not null)") + if err != nil { + t.Fatal(err) + } + + var a string + err = db.QueryRow("INSERT INTO notnulltemp(a) values($1) RETURNING a", nil).Scan(&a) + if err == sql.ErrNoRows { + t.Fatalf("expected constraint violation error; got: %v", err) + } + pge, ok := err.(*Error) + if !ok { + t.Fatalf("expected *Error; got: %#v", err) + } + if pge.Code.Name() != "not_null_violation" { + t.Fatalf("expected not_null_violation; got: %s (%+v)", pge.Code.Name(), err) + } + + // Test workaround in simpleQuery() + tx, err := db.Begin() + if err != nil { + t.Fatalf("unexpected error %s in Begin", err) + } + defer tx.Rollback() + + _, err = tx.Exec("SET LOCAL check_function_bodies TO FALSE") + if err != nil { + t.Fatalf("could not disable check_function_bodies: %s", err) + } + _, err = tx.Exec(` +CREATE OR REPLACE FUNCTION bad_function() +RETURNS integer +-- hack to prevent the function from being inlined +SET check_function_bodies TO TRUE +AS $$ + SELECT text 'bad' +$$ LANGUAGE sql`) + if err != nil { + t.Fatalf("could not create function: %s", err) + } + + err = tx.QueryRow("SELECT * FROM bad_function()").Scan(&a) + if err == nil { + t.Fatalf("expected error") + } + pge, ok = err.(*Error) + if !ok { + t.Fatalf("expected *Error; got: %#v", err) + } + if pge.Code.Name() != "invalid_function_definition" { + t.Fatalf("expected invalid_function_definition; got: %s (%+v)", pge.Code.Name(), err) + } + + err = tx.Rollback() + if err != nil { + t.Fatalf("unexpected error %s in Rollback", err) + } + + // Also test that simpleQuery()'s workaround works when the query fails + // after a row has been received. + rows, err := db.Query(` +select + (select generate_series(1, ss.i)) +from (select gs.i + from generate_series(1, 2) gs(i) + order by gs.i limit 2) ss`) + if err != nil { + t.Fatalf("query failed: %s", err) + } + if !rows.Next() { + t.Fatalf("expected at least one result row; got %s", rows.Err()) + } + var i int + err = rows.Scan(&i) + if err != nil { + t.Fatalf("rows.Scan() failed: %s", err) + } + if i != 1 { + t.Fatalf("unexpected value for i: %d", i) + } + if rows.Next() { + t.Fatalf("unexpected row") + } + pge, ok = rows.Err().(*Error) + if !ok { + t.Fatalf("expected *Error; got: %#v", err) + } + if pge.Code.Name() != "cardinality_violation" { + t.Fatalf("expected cardinality_violation; got: %s (%+v)", pge.Code.Name(), rows.Err()) + } +} + +func TestSimpleQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + r, err := db.Query("select 1") + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if !r.Next() { + t.Fatal("expected row") + } +} + +func TestBindError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("create temp table test (i integer)") + if err != nil { + t.Fatal(err) + } + + _, err = db.Query("select * from test where i=$1", "hhh") + if err == nil { + t.Fatal("expected an error") + } + + // Should not get error here + r, err := db.Query("select * from test where i=$1", 1) + if err != nil { + t.Fatal(err) + } + defer r.Close() +} + +func TestParseErrorInExtendedQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + rows, err := db.Query("PARSE_ERROR $1", 1) + if err == nil { + t.Fatal("expected error") + } + + rows, err = db.Query("SELECT 1") + if err != nil { + t.Fatal(err) + } + rows.Close() +} + +// TestReturning tests that an INSERT query using the RETURNING clause returns a row. +func TestReturning(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("CREATE TEMP TABLE distributors (did integer default 0, dname text)") + if err != nil { + t.Fatal(err) + } + + rows, err := db.Query("INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets') " + + "RETURNING did;") + if err != nil { + t.Fatal(err) + } + if !rows.Next() { + t.Fatal("no rows") + } + var did int + err = rows.Scan(&did) + if err != nil { + t.Fatal(err) + } + if did != 0 { + t.Fatalf("bad value for did: got %d, want %d", did, 0) + } + + if rows.Next() { + t.Fatal("unexpected next row") + } + err = rows.Err() + if err != nil { + t.Fatal(err) + } +} + +func TestIssue186(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + // Exec() a query which returns results + _, err := db.Exec("VALUES (1), (2), (3)") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("VALUES ($1), ($2), ($3)", 1, 2, 3) + if err != nil { + t.Fatal(err) + } + + // Query() a query which doesn't return any results + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + rows, err := txn.Query("CREATE TEMP TABLE foo(f1 int)") + if err != nil { + t.Fatal(err) + } + if err = rows.Close(); err != nil { + t.Fatal(err) + } + + // small trick to get NoData from a parameterized query + _, err = txn.Exec("CREATE RULE nodata AS ON INSERT TO foo DO INSTEAD NOTHING") + if err != nil { + t.Fatal(err) + } + rows, err = txn.Query("INSERT INTO foo VALUES ($1)", 1) + if err != nil { + t.Fatal(err) + } + if err = rows.Close(); err != nil { + t.Fatal(err) + } +} + +func TestIssue196(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + row := db.QueryRow("SELECT float4 '0.10000122' = $1, float8 '35.03554004971999' = $2", + float32(0.10000122), float64(35.03554004971999)) + + var float4match, float8match bool + err := row.Scan(&float4match, &float8match) + if err != nil { + t.Fatal(err) + } + if !float4match { + t.Errorf("Expected float4 fidelity to be maintained; got no match") + } + if !float8match { + t.Errorf("Expected float8 fidelity to be maintained; got no match") + } +} + +// Test that any CommandComplete messages sent before the query results are +// ignored. +func TestIssue282(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + var search_path string + err := db.QueryRow(` + SET LOCAL search_path TO pg_catalog; + SET LOCAL search_path TO pg_catalog; + SHOW search_path`).Scan(&search_path) + if err != nil { + t.Fatal(err) + } + if search_path != "pg_catalog" { + t.Fatalf("unexpected search_path %s", search_path) + } +} + +func TestReadFloatPrecision(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + row := db.QueryRow("SELECT float4 '0.10000122', float8 '35.03554004971999'") + var float4val float32 + var float8val float64 + err := row.Scan(&float4val, &float8val) + if err != nil { + t.Fatal(err) + } + if float4val != float32(0.10000122) { + t.Errorf("Expected float4 fidelity to be maintained; got no match") + } + if float8val != float64(35.03554004971999) { + t.Errorf("Expected float8 fidelity to be maintained; got no match") + } +} + +func TestXactMultiStmt(t *testing.T) { + // minified test case based on bug reports from + // pico303@gmail.com and rangelspam@gmail.com + t.Skip("Skipping failing test") + db := openTestConn(t) + defer db.Close() + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer tx.Commit() + + rows, err := tx.Query("select 1") + if err != nil { + t.Fatal(err) + } + + if rows.Next() { + var val int32 + if err = rows.Scan(&val); err != nil { + t.Fatal(err) + } + } else { + t.Fatal("Expected at least one row in first query in xact") + } + + rows2, err := tx.Query("select 2") + if err != nil { + t.Fatal(err) + } + + if rows2.Next() { + var val2 int32 + if err := rows2.Scan(&val2); err != nil { + t.Fatal(err) + } + } else { + t.Fatal("Expected at least one row in second query in xact") + } + + if err = rows.Err(); err != nil { + t.Fatal(err) + } + + if err = rows2.Err(); err != nil { + t.Fatal(err) + } + + if err = tx.Commit(); err != nil { + t.Fatal(err) + } +} + +var envParseTests = []struct { + Expected map[string]string + Env []string +}{ + { + Env: []string{"PGDATABASE=hello", "PGUSER=goodbye"}, + Expected: map[string]string{"dbname": "hello", "user": "goodbye"}, + }, + { + Env: []string{"PGDATESTYLE=ISO, MDY"}, + Expected: map[string]string{"datestyle": "ISO, MDY"}, + }, + { + Env: []string{"PGCONNECT_TIMEOUT=30"}, + Expected: map[string]string{"connect_timeout": "30"}, + }, +} + +func TestParseEnviron(t *testing.T) { + for i, tt := range envParseTests { + results := parseEnviron(tt.Env) + if !reflect.DeepEqual(tt.Expected, results) { + t.Errorf("%d: Expected: %#v Got: %#v", i, tt.Expected, results) + } + } +} + +func TestParseComplete(t *testing.T) { + tpc := func(commandTag string, command string, affectedRows int64, shouldFail bool) { + defer func() { + if p := recover(); p != nil { + if !shouldFail { + t.Error(p) + } + } + }() + cn := &conn{} + res, c := cn.parseComplete(commandTag) + if c != command { + t.Errorf("Expected %v, got %v", command, c) + } + n, err := res.RowsAffected() + if err != nil { + t.Fatal(err) + } + if n != affectedRows { + t.Errorf("Expected %d, got %d", affectedRows, n) + } + } + + tpc("ALTER TABLE", "ALTER TABLE", 0, false) + tpc("INSERT 0 1", "INSERT", 1, false) + tpc("UPDATE 100", "UPDATE", 100, false) + tpc("SELECT 100", "SELECT", 100, false) + tpc("FETCH 100", "FETCH", 100, false) + // allow COPY (and others) without row count + tpc("COPY", "COPY", 0, false) + // don't fail on command tags we don't recognize + tpc("UNKNOWNCOMMANDTAG", "UNKNOWNCOMMANDTAG", 0, false) + + // failure cases + tpc("INSERT 1", "", 0, true) // missing oid + tpc("UPDATE 0 1", "", 0, true) // too many numbers + tpc("SELECT foo", "", 0, true) // invalid row count +} + +func TestExecerInterface(t *testing.T) { + // Gin up a straw man private struct just for the type check + cn := &conn{c: nil} + var cni interface{} = cn + + _, ok := cni.(driver.Execer) + if !ok { + t.Fatal("Driver doesn't implement Execer") + } +} + +func TestNullAfterNonNull(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + r, err := db.Query("SELECT 9::integer UNION SELECT NULL::integer") + if err != nil { + t.Fatal(err) + } + + var n sql.NullInt64 + + if !r.Next() { + if r.Err() != nil { + t.Fatal(err) + } + t.Fatal("expected row") + } + + if err := r.Scan(&n); err != nil { + t.Fatal(err) + } + + if n.Int64 != 9 { + t.Fatalf("expected 2, not %d", n.Int64) + } + + if !r.Next() { + if r.Err() != nil { + t.Fatal(err) + } + t.Fatal("expected row") + } + + if err := r.Scan(&n); err != nil { + t.Fatal(err) + } + + if n.Valid { + t.Fatal("expected n to be invalid") + } + + if n.Int64 != 0 { + t.Fatalf("expected n to 2, not %d", n.Int64) + } +} + +func Test64BitErrorChecking(t *testing.T) { + defer func() { + if err := recover(); err != nil { + t.Fatal("panic due to 0xFFFFFFFF != -1 " + + "when int is 64 bits") + } + }() + + db := openTestConn(t) + defer db.Close() + + r, err := db.Query(`SELECT * +FROM (VALUES (0::integer, NULL::text), (1, 'test string')) AS t;`) + + if err != nil { + t.Fatal(err) + } + + defer r.Close() + + for r.Next() { + } +} + +func TestCommit(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("CREATE TEMP TABLE temp (a int)") + if err != nil { + t.Fatal(err) + } + sqlInsert := "INSERT INTO temp VALUES (1)" + sqlSelect := "SELECT * FROM temp" + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + _, err = tx.Exec(sqlInsert) + if err != nil { + t.Fatal(err) + } + err = tx.Commit() + if err != nil { + t.Fatal(err) + } + var i int + err = db.QueryRow(sqlSelect).Scan(&i) + if err != nil { + t.Fatal(err) + } + if i != 1 { + t.Fatalf("expected 1, got %d", i) + } +} + +func TestErrorClass(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Query("SELECT int 'notint'") + if err == nil { + t.Fatal("expected error") + } + pge, ok := err.(*Error) + if !ok { + t.Fatalf("expected *pq.Error, got %#+v", err) + } + if pge.Code.Class() != "22" { + t.Fatalf("expected class 28, got %v", pge.Code.Class()) + } + if pge.Code.Class().Name() != "data_exception" { + t.Fatalf("expected data_exception, got %v", pge.Code.Class().Name()) + } +} + +func TestParseOpts(t *testing.T) { + tests := []struct { + in string + expected values + valid bool + }{ + {"dbname=hello user=goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, + {"dbname=hello user=goodbye ", values{"dbname": "hello", "user": "goodbye"}, true}, + {"dbname = hello user=goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, + {"dbname=hello user =goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, + {"dbname=hello user= goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, + {"host=localhost password='correct horse battery staple'", values{"host": "localhost", "password": "correct horse battery staple"}, true}, + {"dbname=データベース password=パスワード", values{"dbname": "データベース", "password": "パスワード"}, true}, + {"dbname=hello user=''", values{"dbname": "hello", "user": ""}, true}, + {"user='' dbname=hello", values{"dbname": "hello", "user": ""}, true}, + // The last option value is an empty string if there's no non-whitespace after its = + {"dbname=hello user= ", values{"dbname": "hello", "user": ""}, true}, + + // The parser ignores spaces after = and interprets the next set of non-whitespace characters as the value. + {"user= password=foo", values{"user": "password=foo"}, true}, + + // Backslash escapes next char + {`user=a\ \'\\b`, values{"user": `a '\b`}, true}, + {`user='a \'b'`, values{"user": `a 'b`}, true}, + + // Incomplete escape + {`user=x\`, values{}, false}, + + // No '=' after the key + {"postgre://marko@internet", values{}, false}, + {"dbname user=goodbye", values{}, false}, + {"user=foo blah", values{}, false}, + {"user=foo blah ", values{}, false}, + + // Unterminated quoted value + {"dbname=hello user='unterminated", values{}, false}, + } + + for _, test := range tests { + o := make(values) + err := parseOpts(test.in, o) + + switch { + case err != nil && test.valid: + t.Errorf("%q got unexpected error: %s", test.in, err) + case err == nil && test.valid && !reflect.DeepEqual(test.expected, o): + t.Errorf("%q got: %#v want: %#v", test.in, o, test.expected) + case err == nil && !test.valid: + t.Errorf("%q expected an error", test.in) + } + } +} + +func TestRuntimeParameters(t *testing.T) { + type RuntimeTestResult int + const ( + ResultUnknown RuntimeTestResult = iota + ResultSuccess + ResultError // other error + ) + + tests := []struct { + conninfo string + param string + expected string + expectedOutcome RuntimeTestResult + }{ + // invalid parameter + {"DOESNOTEXIST=foo", "", "", ResultError}, + // we can only work with a specific value for these two + {"client_encoding=SQL_ASCII", "", "", ResultError}, + {"datestyle='ISO, YDM'", "", "", ResultError}, + // "options" should work exactly as it does in libpq + {"options='-c search_path=pqgotest'", "search_path", "pqgotest", ResultSuccess}, + // pq should override client_encoding in this case + {"options='-c client_encoding=SQL_ASCII'", "client_encoding", "UTF8", ResultSuccess}, + // allow client_encoding to be set explicitly + {"client_encoding=UTF8", "client_encoding", "UTF8", ResultSuccess}, + // test a runtime parameter not supported by libpq + {"work_mem='139kB'", "work_mem", "139kB", ResultSuccess}, + // test fallback_application_name + {"application_name=foo fallback_application_name=bar", "application_name", "foo", ResultSuccess}, + {"application_name='' fallback_application_name=bar", "application_name", "", ResultSuccess}, + {"fallback_application_name=bar", "application_name", "bar", ResultSuccess}, + } + + for _, test := range tests { + db, err := openTestConnConninfo(test.conninfo) + if err != nil { + t.Fatal(err) + } + + // application_name didn't exist before 9.0 + if test.param == "application_name" && getServerVersion(t, db) < 90000 { + db.Close() + continue + } + + tryGetParameterValue := func() (value string, outcome RuntimeTestResult) { + defer db.Close() + row := db.QueryRow("SELECT current_setting($1)", test.param) + err = row.Scan(&value) + if err != nil { + return "", ResultError + } + return value, ResultSuccess + } + + value, outcome := tryGetParameterValue() + if outcome != test.expectedOutcome && outcome == ResultError { + t.Fatalf("%v: unexpected error: %v", test.conninfo, err) + } + if outcome != test.expectedOutcome { + t.Fatalf("unexpected outcome %v (was expecting %v) for conninfo \"%s\"", + outcome, test.expectedOutcome, test.conninfo) + } + if value != test.expected { + t.Fatalf("bad value for %s: got %s, want %s with conninfo \"%s\"", + test.param, value, test.expected, test.conninfo) + } + } +} + +func TestIsUTF8(t *testing.T) { + var cases = []struct { + name string + want bool + }{ + {"unicode", true}, + {"utf-8", true}, + {"utf_8", true}, + {"UTF-8", true}, + {"UTF8", true}, + {"utf8", true}, + {"u n ic_ode", true}, + {"ut_f%8", true}, + {"ubf8", false}, + {"punycode", false}, + } + + for _, test := range cases { + if g := isUTF8(test.name); g != test.want { + t.Errorf("isUTF8(%q) = %v want %v", test.name, g, test.want) + } + } +} + +func TestQuoteIdentifier(t *testing.T) { + var cases = []struct { + input string + want string + }{ + {`foo`, `"foo"`}, + {`foo bar baz`, `"foo bar baz"`}, + {`foo"bar`, `"foo""bar"`}, + {"foo\x00bar", `"foo"`}, + {"\x00foo", `""`}, + } + + for _, test := range cases { + got := QuoteIdentifier(test.input) + if got != test.want { + t.Errorf("QuoteIdentifier(%q) = %v want %v", test.input, got, test.want) + } + } +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/copy.go b/Godeps/_workspace/src/github.com/lib/pq/copy.go new file mode 100644 index 000000000..e44fa48a5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/copy.go @@ -0,0 +1,268 @@ +package pq + +import ( + "database/sql/driver" + "encoding/binary" + "errors" + "fmt" + "sync" +) + +var ( + errCopyInClosed = errors.New("pq: copyin statement has already been closed") + errBinaryCopyNotSupported = errors.New("pq: only text format supported for COPY") + errCopyToNotSupported = errors.New("pq: COPY TO is not supported") + errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction") +) + +// CopyIn creates a COPY FROM statement which can be prepared with +// Tx.Prepare(). The target table should be visible in search_path. +func CopyIn(table string, columns ...string) string { + stmt := "COPY " + QuoteIdentifier(table) + " (" + for i, col := range columns { + if i != 0 { + stmt += ", " + } + stmt += QuoteIdentifier(col) + } + stmt += ") FROM STDIN" + return stmt +} + +// CopyInSchema creates a COPY FROM statement which can be prepared with +// Tx.Prepare(). +func CopyInSchema(schema, table string, columns ...string) string { + stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " (" + for i, col := range columns { + if i != 0 { + stmt += ", " + } + stmt += QuoteIdentifier(col) + } + stmt += ") FROM STDIN" + return stmt +} + +type copyin struct { + cn *conn + buffer []byte + rowData chan []byte + done chan bool + + closed bool + + sync.Mutex // guards err + err error +} + +const ciBufferSize = 64 * 1024 + +// flush buffer before the buffer is filled up and needs reallocation +const ciBufferFlushSize = 63 * 1024 + +func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) { + if !cn.isInTransaction() { + return nil, errCopyNotSupportedOutsideTxn + } + + ci := ©in{ + cn: cn, + buffer: make([]byte, 0, ciBufferSize), + rowData: make(chan []byte), + done: make(chan bool, 1), + } + // add CopyData identifier + 4 bytes for message length + ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0) + + b := cn.writeBuf('Q') + b.string(q) + cn.send(b) + +awaitCopyInResponse: + for { + t, r := cn.recv1() + switch t { + case 'G': + if r.byte() != 0 { + err = errBinaryCopyNotSupported + break awaitCopyInResponse + } + go ci.resploop() + return ci, nil + case 'H': + err = errCopyToNotSupported + break awaitCopyInResponse + case 'E': + err = parseError(r) + case 'Z': + if err == nil { + cn.bad = true + errorf("unexpected ReadyForQuery in response to COPY") + } + cn.processReadyForQuery(r) + return nil, err + default: + cn.bad = true + errorf("unknown response for copy query: %q", t) + } + } + + // something went wrong, abort COPY before we return + b = cn.writeBuf('f') + b.string(err.Error()) + cn.send(b) + + for { + t, r := cn.recv1() + switch t { + case 'c', 'C', 'E': + case 'Z': + // correctly aborted, we're done + cn.processReadyForQuery(r) + return nil, err + default: + cn.bad = true + errorf("unknown response for CopyFail: %q", t) + } + } +} + +func (ci *copyin) flush(buf []byte) { + // set message length (without message identifier) + binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1)) + + _, err := ci.cn.c.Write(buf) + if err != nil { + panic(err) + } +} + +func (ci *copyin) resploop() { + for { + var r readBuf + t, err := ci.cn.recvMessage(&r) + if err != nil { + ci.cn.bad = true + ci.setError(err) + ci.done <- true + return + } + switch t { + case 'C': + // complete + case 'N': + // NoticeResponse + case 'Z': + ci.cn.processReadyForQuery(&r) + ci.done <- true + return + case 'E': + err := parseError(&r) + ci.setError(err) + default: + ci.cn.bad = true + ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t)) + ci.done <- true + return + } + } +} + +func (ci *copyin) isErrorSet() bool { + ci.Lock() + isSet := (ci.err != nil) + ci.Unlock() + return isSet +} + +// setError() sets ci.err if one has not been set already. Caller must not be +// holding ci.Mutex. +func (ci *copyin) setError(err error) { + ci.Lock() + if ci.err == nil { + ci.err = err + } + ci.Unlock() +} + +func (ci *copyin) NumInput() int { + return -1 +} + +func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) { + return nil, ErrNotSupported +} + +// Exec inserts values into the COPY stream. The insert is asynchronous +// and Exec can return errors from previous Exec calls to the same +// COPY stmt. +// +// You need to call Exec(nil) to sync the COPY stream and to get any +// errors from pending data, since Stmt.Close() doesn't return errors +// to the user. +func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) { + if ci.closed { + return nil, errCopyInClosed + } + + if ci.cn.bad { + return nil, driver.ErrBadConn + } + defer ci.cn.errRecover(&err) + + if ci.isErrorSet() { + return nil, ci.err + } + + if len(v) == 0 { + err = ci.Close() + ci.closed = true + return nil, err + } + + numValues := len(v) + for i, value := range v { + ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value) + if i < numValues-1 { + ci.buffer = append(ci.buffer, '\t') + } + } + + ci.buffer = append(ci.buffer, '\n') + + if len(ci.buffer) > ciBufferFlushSize { + ci.flush(ci.buffer) + // reset buffer, keep bytes for message identifier and length + ci.buffer = ci.buffer[:5] + } + + return driver.RowsAffected(0), nil +} + +func (ci *copyin) Close() (err error) { + if ci.closed { + return errCopyInClosed + } + + if ci.cn.bad { + return driver.ErrBadConn + } + defer ci.cn.errRecover(&err) + + if len(ci.buffer) > 0 { + ci.flush(ci.buffer) + } + // Avoid touching the scratch buffer as resploop could be using it. + err = ci.cn.sendSimpleMessage('c') + if err != nil { + return err + } + + <-ci.done + + if ci.isErrorSet() { + err = ci.err + return err + } + return nil +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/copy_test.go b/Godeps/_workspace/src/github.com/lib/pq/copy_test.go new file mode 100644 index 000000000..6af4c9c76 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/copy_test.go @@ -0,0 +1,462 @@ +package pq + +import ( + "bytes" + "database/sql" + "strings" + "testing" +) + +func TestCopyInStmt(t *testing.T) { + var stmt string + stmt = CopyIn("table name") + if stmt != `COPY "table name" () FROM STDIN` { + t.Fatal(stmt) + } + + stmt = CopyIn("table name", "column 1", "column 2") + if stmt != `COPY "table name" ("column 1", "column 2") FROM STDIN` { + t.Fatal(stmt) + } + + stmt = CopyIn(`table " name """`, `co"lumn""`) + if stmt != `COPY "table "" name """"""" ("co""lumn""""") FROM STDIN` { + t.Fatal(stmt) + } +} + +func TestCopyInSchemaStmt(t *testing.T) { + var stmt string + stmt = CopyInSchema("schema name", "table name") + if stmt != `COPY "schema name"."table name" () FROM STDIN` { + t.Fatal(stmt) + } + + stmt = CopyInSchema("schema name", "table name", "column 1", "column 2") + if stmt != `COPY "schema name"."table name" ("column 1", "column 2") FROM STDIN` { + t.Fatal(stmt) + } + + stmt = CopyInSchema(`schema " name """`, `table " name """`, `co"lumn""`) + if stmt != `COPY "schema "" name """"""".`+ + `"table "" name """"""" ("co""lumn""""") FROM STDIN` { + t.Fatal(stmt) + } +} + +func TestCopyInMultipleValues(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)") + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "a", "b")) + if err != nil { + t.Fatal(err) + } + + longString := strings.Repeat("#", 500) + + for i := 0; i < 500; i++ { + _, err = stmt.Exec(int64(i), longString) + if err != nil { + t.Fatal(err) + } + } + + _, err = stmt.Exec() + if err != nil { + t.Fatal(err) + } + + err = stmt.Close() + if err != nil { + t.Fatal(err) + } + + var num int + err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num) + if err != nil { + t.Fatal(err) + } + + if num != 500 { + t.Fatalf("expected 500 items, not %d", num) + } +} + +func TestCopyInRaiseStmtTrigger(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + if getServerVersion(t, db) < 90000 { + var exists int + err := db.QueryRow("SELECT 1 FROM pg_language WHERE lanname = 'plpgsql'").Scan(&exists) + if err == sql.ErrNoRows { + t.Skip("language PL/PgSQL does not exist; skipping TestCopyInRaiseStmtTrigger") + } else if err != nil { + t.Fatal(err) + } + } + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)") + if err != nil { + t.Fatal(err) + } + + _, err = txn.Exec(` + CREATE OR REPLACE FUNCTION pg_temp.temptest() + RETURNS trigger AS + $BODY$ begin + raise notice 'Hello world'; + return new; + end $BODY$ + LANGUAGE plpgsql`) + if err != nil { + t.Fatal(err) + } + + _, err = txn.Exec(` + CREATE TRIGGER temptest_trigger + BEFORE INSERT + ON temp + FOR EACH ROW + EXECUTE PROCEDURE pg_temp.temptest()`) + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "a", "b")) + if err != nil { + t.Fatal(err) + } + + longString := strings.Repeat("#", 500) + + _, err = stmt.Exec(int64(1), longString) + if err != nil { + t.Fatal(err) + } + + _, err = stmt.Exec() + if err != nil { + t.Fatal(err) + } + + err = stmt.Close() + if err != nil { + t.Fatal(err) + } + + var num int + err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num) + if err != nil { + t.Fatal(err) + } + + if num != 1 { + t.Fatalf("expected 1 items, not %d", num) + } +} + +func TestCopyInTypes(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER, text VARCHAR, blob BYTEA, nothing VARCHAR)") + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "num", "text", "blob", "nothing")) + if err != nil { + t.Fatal(err) + } + + _, err = stmt.Exec(int64(1234567890), "Héllö\n ☃!\r\t\\", []byte{0, 255, 9, 10, 13}, nil) + if err != nil { + t.Fatal(err) + } + + _, err = stmt.Exec() + if err != nil { + t.Fatal(err) + } + + err = stmt.Close() + if err != nil { + t.Fatal(err) + } + + var num int + var text string + var blob []byte + var nothing sql.NullString + + err = txn.QueryRow("SELECT * FROM temp").Scan(&num, &text, &blob, ¬hing) + if err != nil { + t.Fatal(err) + } + + if num != 1234567890 { + t.Fatal("unexpected result", num) + } + if text != "Héllö\n ☃!\r\t\\" { + t.Fatal("unexpected result", text) + } + if bytes.Compare(blob, []byte{0, 255, 9, 10, 13}) != 0 { + t.Fatal("unexpected result", blob) + } + if nothing.Valid { + t.Fatal("unexpected result", nothing.String) + } +} + +func TestCopyInWrongType(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)") + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "num")) + if err != nil { + t.Fatal(err) + } + defer stmt.Close() + + _, err = stmt.Exec("Héllö\n ☃!\r\t\\") + if err != nil { + t.Fatal(err) + } + + _, err = stmt.Exec() + if err == nil { + t.Fatal("expected error") + } + if pge := err.(*Error); pge.Code.Name() != "invalid_text_representation" { + t.Fatalf("expected 'invalid input syntax for integer' error, got %s (%+v)", pge.Code.Name(), pge) + } +} + +func TestCopyOutsideOfTxnError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Prepare(CopyIn("temp", "num")) + if err == nil { + t.Fatal("COPY outside of transaction did not return an error") + } + if err != errCopyNotSupportedOutsideTxn { + t.Fatalf("expected %s, got %s", err, err.Error()) + } +} + +func TestCopyInBinaryError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)") + if err != nil { + t.Fatal(err) + } + _, err = txn.Prepare("COPY temp (num) FROM STDIN WITH binary") + if err != errBinaryCopyNotSupported { + t.Fatalf("expected %s, got %+v", errBinaryCopyNotSupported, err) + } + // check that the protocol is in a valid state + err = txn.Rollback() + if err != nil { + t.Fatal(err) + } +} + +func TestCopyFromError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)") + if err != nil { + t.Fatal(err) + } + _, err = txn.Prepare("COPY temp (num) TO STDOUT") + if err != errCopyToNotSupported { + t.Fatalf("expected %s, got %+v", errCopyToNotSupported, err) + } + // check that the protocol is in a valid state + err = txn.Rollback() + if err != nil { + t.Fatal(err) + } +} + +func TestCopySyntaxError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Prepare("COPY ") + if err == nil { + t.Fatal("expected error") + } + if pge := err.(*Error); pge.Code.Name() != "syntax_error" { + t.Fatalf("expected syntax error, got %s (%+v)", pge.Code.Name(), pge) + } + // check that the protocol is in a valid state + err = txn.Rollback() + if err != nil { + t.Fatal(err) + } +} + +// Tests for connection errors in copyin.resploop() +func TestCopyRespLoopConnectionError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + var pid int + err = txn.QueryRow("SELECT pg_backend_pid()").Scan(&pid) + if err != nil { + t.Fatal(err) + } + + _, err = txn.Exec("CREATE TEMP TABLE temp (a int)") + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "a")) + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("SELECT pg_terminate_backend($1)", pid) + if err != nil { + t.Fatal(err) + } + + if getServerVersion(t, db) < 90500 { + // We have to try and send something over, since postgres before + // version 9.5 won't process SIGTERMs while it's waiting for + // CopyData/CopyEnd messages; see tcop/postgres.c. + _, err = stmt.Exec(1) + if err != nil { + t.Fatal(err) + } + } + _, err = stmt.Exec() + if err == nil { + t.Fatalf("expected error") + } + pge, ok := err.(*Error) + if !ok { + t.Fatalf("expected *pq.Error, got %+#v", err) + } else if pge.Code.Name() != "admin_shutdown" { + t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name()) + } + + err = stmt.Close() + if err != nil { + t.Fatal(err) + } +} + +func BenchmarkCopyIn(b *testing.B) { + db := openTestConn(b) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + b.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)") + if err != nil { + b.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "a", "b")) + if err != nil { + b.Fatal(err) + } + + for i := 0; i < b.N; i++ { + _, err = stmt.Exec(int64(i), "hello world!") + if err != nil { + b.Fatal(err) + } + } + + _, err = stmt.Exec() + if err != nil { + b.Fatal(err) + } + + err = stmt.Close() + if err != nil { + b.Fatal(err) + } + + var num int + err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num) + if err != nil { + b.Fatal(err) + } + + if num != b.N { + b.Fatalf("expected %d items, not %d", b.N, num) + } +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/doc.go b/Godeps/_workspace/src/github.com/lib/pq/doc.go new file mode 100644 index 000000000..f772117d0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/doc.go @@ -0,0 +1,210 @@ +/* +Package pq is a pure Go Postgres driver for the database/sql package. + +In most cases clients will use the database/sql package instead of +using this package directly. For example: + + import ( + "database/sql" + + _ "github.com/lib/pq" + ) + + func main() { + db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=verify-full") + if err != nil { + log.Fatal(err) + } + + age := 21 + rows, err := db.Query("SELECT name FROM users WHERE age = $1", age) + … + } + +You can also connect to a database using a URL. For example: + + db, err := sql.Open("postgres", "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full") + + +Connection String Parameters + + +Similarly to libpq, when establishing a connection using pq you are expected to +supply a connection string containing zero or more parameters. +A subset of the connection parameters supported by libpq are also supported by pq. +Additionally, pq also lets you specify run-time parameters (such as search_path or work_mem) +directly in the connection string. This is different from libpq, which does not allow +run-time parameters in the connection string, instead requiring you to supply +them in the options parameter. + +For compatibility with libpq, the following special connection parameters are +supported: + + * dbname - The name of the database to connect to + * user - The user to sign in as + * password - The user's password + * host - The host to connect to. Values that start with / are for unix domain sockets. (default is localhost) + * port - The port to bind to. (default is 5432) + * sslmode - Whether or not to use SSL (default is require, this is not the default for libpq) + * fallback_application_name - An application_name to fall back to if one isn't provided. + * connect_timeout - Maximum wait for connection, in seconds. Zero or not specified means wait indefinitely. + * sslcert - Cert file location. The file must contain PEM encoded data. + * sslkey - Key file location. The file must contain PEM encoded data. + * sslrootcert - The location of the root certificate file. The file must contain PEM encoded data. + +Valid values for sslmode are: + + * disable - No SSL + * require - Always SSL (skip verification) + * verify-ca - Always SSL (verify that the certificate presented by the server was signed by a trusted CA) + * verify-full - Always SSL (verify that the certification presented by the server was signed by a trusted CA and the server host name matches the one in the certificate) + +See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING +for more information about connection string parameters. + +Use single quotes for values that contain whitespace: + + "user=pqgotest password='with spaces'" + +A backslash will escape the next character in values: + + "user=space\ man password='it\'s valid' + +Note that the connection parameter client_encoding (which sets the +text encoding for the connection) may be set but must be "UTF8", +matching with the same rules as Postgres. It is an error to provide +any other value. + +In addition to the parameters listed above, any run-time parameter that can be +set at backend start time can be set in the connection string. For more +information, see +http://www.postgresql.org/docs/current/static/runtime-config.html. + +Most environment variables as specified at http://www.postgresql.org/docs/current/static/libpq-envars.html +supported by libpq are also supported by pq. If any of the environment +variables not supported by pq are set, pq will panic during connection +establishment. Environment variables have a lower precedence than explicitly +provided connection parameters. + + +Queries + +database/sql does not dictate any specific format for parameter +markers in query strings, and pq uses the Postgres-native ordinal markers, +as shown above. The same marker can be reused for the same parameter: + + rows, err := db.Query(`SELECT name FROM users WHERE favorite_fruit = $1 + OR age BETWEEN $2 AND $2 + 3`, "orange", 64) + +pq does not support the LastInsertId() method of the Result type in database/sql. +To return the identifier of an INSERT (or UPDATE or DELETE), use the Postgres +RETURNING clause with a standard Query or QueryRow call: + + var userid int + err := db.QueryRow(`INSERT INTO users(name, favorite_fruit, age) + VALUES('beatrice', 'starfruit', 93) RETURNING id`).Scan(&userid) + +For more details on RETURNING, see the Postgres documentation: + + http://www.postgresql.org/docs/current/static/sql-insert.html + http://www.postgresql.org/docs/current/static/sql-update.html + http://www.postgresql.org/docs/current/static/sql-delete.html + +For additional instructions on querying see the documentation for the database/sql package. + +Errors + +pq may return errors of type *pq.Error which can be interrogated for error details: + + if err, ok := err.(*pq.Error); ok { + fmt.Println("pq error:", err.Code.Name()) + } + +See the pq.Error type for details. + + +Bulk imports + +You can perform bulk imports by preparing a statement returned by pq.CopyIn (or +pq.CopyInSchema) in an explicit transaction (sql.Tx). The returned statement +handle can then be repeatedly "executed" to copy data into the target table. +After all data has been processed you should call Exec() once with no arguments +to flush all buffered data. Any call to Exec() might return an error which +should be handled appropriately, but because of the internal buffering an error +returned by Exec() might not be related to the data passed in the call that +failed. + +CopyIn uses COPY FROM internally. It is not possible to COPY outside of an +explicit transaction in pq. + +Usage example: + + txn, err := db.Begin() + if err != nil { + log.Fatal(err) + } + + stmt, err := txn.Prepare(pq.CopyIn("users", "name", "age")) + if err != nil { + log.Fatal(err) + } + + for _, user := range users { + _, err = stmt.Exec(user.Name, int64(user.Age)) + if err != nil { + log.Fatal(err) + } + } + + _, err = stmt.Exec() + if err != nil { + log.Fatal(err) + } + + err = stmt.Close() + if err != nil { + log.Fatal(err) + } + + err = txn.Commit() + if err != nil { + log.Fatal(err) + } + + +Notifications + + +PostgreSQL supports a simple publish/subscribe model over database +connections. See http://www.postgresql.org/docs/current/static/sql-notify.html +for more information about the general mechanism. + +To start listening for notifications, you first have to open a new connection +to the database by calling NewListener. This connection can not be used for +anything other than LISTEN / NOTIFY. Calling Listen will open a "notification +channel"; once a notification channel is open, a notification generated on that +channel will effect a send on the Listener.Notify channel. A notification +channel will remain open until Unlisten is called, though connection loss might +result in some notifications being lost. To solve this problem, Listener sends +a nil pointer over the Notify channel any time the connection is re-established +following a connection loss. The application can get information about the +state of the underlying connection by setting an event callback in the call to +NewListener. + +A single Listener can safely be used from concurrent goroutines, which means +that there is often no need to create more than one Listener in your +application. However, a Listener is always connected to a single database, so +you will need to create a new Listener instance for every database you want to +receive notifications in. + +The channel name in both Listen and Unlisten is case sensitive, and can contain +any characters legal in an identifier (see +http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS +for more information). Note that the channel name will be truncated to 63 +bytes by the PostgreSQL server. + +You can find a complete, working example of Listener usage at +http://godoc.org/github.com/lib/pq/listen_example. + +*/ +package pq diff --git a/Godeps/_workspace/src/github.com/lib/pq/encode.go b/Godeps/_workspace/src/github.com/lib/pq/encode.go new file mode 100644 index 000000000..ad5f9683f --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/encode.go @@ -0,0 +1,501 @@ +package pq + +import ( + "bytes" + "database/sql/driver" + "encoding/hex" + "fmt" + "math" + "strconv" + "strings" + "sync" + "time" + + "github.com/lib/pq/oid" +) + +func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) []byte { + switch v := x.(type) { + case int64: + return strconv.AppendInt(nil, v, 10) + case float64: + return strconv.AppendFloat(nil, v, 'f', -1, 64) + case []byte: + if pgtypOid == oid.T_bytea { + return encodeBytea(parameterStatus.serverVersion, v) + } + + return v + case string: + if pgtypOid == oid.T_bytea { + return encodeBytea(parameterStatus.serverVersion, []byte(v)) + } + + return []byte(v) + case bool: + return strconv.AppendBool(nil, v) + case time.Time: + return formatTs(v) + + default: + errorf("encode: unknown type for %T", v) + } + + panic("not reached") +} + +func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} { + switch typ { + case oid.T_bytea: + return parseBytea(s) + case oid.T_timestamptz: + return parseTs(parameterStatus.currentLocation, string(s)) + case oid.T_timestamp, oid.T_date: + return parseTs(nil, string(s)) + case oid.T_time: + return mustParse("15:04:05", typ, s) + case oid.T_timetz: + return mustParse("15:04:05-07", typ, s) + case oid.T_bool: + return s[0] == 't' + case oid.T_int8, oid.T_int2, oid.T_int4: + i, err := strconv.ParseInt(string(s), 10, 64) + if err != nil { + errorf("%s", err) + } + return i + case oid.T_float4, oid.T_float8: + bits := 64 + if typ == oid.T_float4 { + bits = 32 + } + f, err := strconv.ParseFloat(string(s), bits) + if err != nil { + errorf("%s", err) + } + return f + } + + return s +} + +// appendEncodedText encodes item in text format as required by COPY +// and appends to buf +func appendEncodedText(parameterStatus *parameterStatus, buf []byte, x interface{}) []byte { + switch v := x.(type) { + case int64: + return strconv.AppendInt(buf, v, 10) + case float64: + return strconv.AppendFloat(buf, v, 'f', -1, 64) + case []byte: + encodedBytea := encodeBytea(parameterStatus.serverVersion, v) + return appendEscapedText(buf, string(encodedBytea)) + case string: + return appendEscapedText(buf, v) + case bool: + return strconv.AppendBool(buf, v) + case time.Time: + return append(buf, formatTs(v)...) + case nil: + return append(buf, "\\N"...) + default: + errorf("encode: unknown type for %T", v) + } + + panic("not reached") +} + +func appendEscapedText(buf []byte, text string) []byte { + escapeNeeded := false + startPos := 0 + var c byte + + // check if we need to escape + for i := 0; i < len(text); i++ { + c = text[i] + if c == '\\' || c == '\n' || c == '\r' || c == '\t' { + escapeNeeded = true + startPos = i + break + } + } + if !escapeNeeded { + return append(buf, text...) + } + + // copy till first char to escape, iterate the rest + result := append(buf, text[:startPos]...) + for i := startPos; i < len(text); i++ { + c = text[i] + switch c { + case '\\': + result = append(result, '\\', '\\') + case '\n': + result = append(result, '\\', 'n') + case '\r': + result = append(result, '\\', 'r') + case '\t': + result = append(result, '\\', 't') + default: + result = append(result, c) + } + } + return result +} + +func mustParse(f string, typ oid.Oid, s []byte) time.Time { + str := string(s) + + // check for a 30-minute-offset timezone + if (typ == oid.T_timestamptz || typ == oid.T_timetz) && + str[len(str)-3] == ':' { + f += ":00" + } + t, err := time.Parse(f, str) + if err != nil { + errorf("decode: %s", err) + } + return t +} + +func expect(str, char string, pos int) { + if c := str[pos : pos+1]; c != char { + errorf("expected '%v' at position %v; got '%v'", char, pos, c) + } +} + +func mustAtoi(str string) int { + result, err := strconv.Atoi(str) + if err != nil { + errorf("expected number; got '%v'", str) + } + return result +} + +// The location cache caches the time zones typically used by the client. +type locationCache struct { + cache map[int]*time.Location + lock sync.Mutex +} + +// All connections share the same list of timezones. Benchmarking shows that +// about 5% speed could be gained by putting the cache in the connection and +// losing the mutex, at the cost of a small amount of memory and a somewhat +// significant increase in code complexity. +var globalLocationCache *locationCache = newLocationCache() + +func newLocationCache() *locationCache { + return &locationCache{cache: make(map[int]*time.Location)} +} + +// Returns the cached timezone for the specified offset, creating and caching +// it if necessary. +func (c *locationCache) getLocation(offset int) *time.Location { + c.lock.Lock() + defer c.lock.Unlock() + + location, ok := c.cache[offset] + if !ok { + location = time.FixedZone("", offset) + c.cache[offset] = location + } + + return location +} + +var infinityTsEnabled = false +var infinityTsNegative time.Time +var infinityTsPositive time.Time + +const ( + infinityTsEnabledAlready = "pq: infinity timestamp enabled already" + infinityTsNegativeMustBeSmaller = "pq: infinity timestamp: negative value must be smaller (before) than positive" +) + +/* + * If EnableInfinityTs is not called, "-infinity" and "infinity" will return + * []byte("-infinity") and []byte("infinity") respectively, and potentially + * cause error "sql: Scan error on column index 0: unsupported driver -> Scan pair: []uint8 -> *time.Time", + * when scanning into a time.Time value. + * + * Once EnableInfinityTs has been called, all connections created using this + * driver will decode Postgres' "-infinity" and "infinity" for "timestamp", + * "timestamp with time zone" and "date" types to the predefined minimum and + * maximum times, respectively. When encoding time.Time values, any time which + * equals or preceeds the predefined minimum time will be encoded to + * "-infinity". Any values at or past the maximum time will similarly be + * encoded to "infinity". + * + * + * If EnableInfinityTs is called with negative >= positive, it will panic. + * Calling EnableInfinityTs after a connection has been established results in + * undefined behavior. If EnableInfinityTs is called more than once, it will + * panic. + */ +func EnableInfinityTs(negative time.Time, positive time.Time) { + if infinityTsEnabled { + panic(infinityTsEnabledAlready) + } + if !negative.Before(positive) { + panic(infinityTsNegativeMustBeSmaller) + } + infinityTsEnabled = true + infinityTsNegative = negative + infinityTsPositive = positive +} + +/* + * Testing might want to toggle infinityTsEnabled + */ +func disableInfinityTs() { + infinityTsEnabled = false +} + +// This is a time function specific to the Postgres default DateStyle +// setting ("ISO, MDY"), the only one we currently support. This +// accounts for the discrepancies between the parsing available with +// time.Parse and the Postgres date formatting quirks. +func parseTs(currentLocation *time.Location, str string) interface{} { + switch str { + case "-infinity": + if infinityTsEnabled { + return infinityTsNegative + } + return []byte(str) + case "infinity": + if infinityTsEnabled { + return infinityTsPositive + } + return []byte(str) + } + + monSep := strings.IndexRune(str, '-') + // this is Gregorian year, not ISO Year + // In Gregorian system, the year 1 BC is followed by AD 1 + year := mustAtoi(str[:monSep]) + daySep := monSep + 3 + month := mustAtoi(str[monSep+1 : daySep]) + expect(str, "-", daySep) + timeSep := daySep + 3 + day := mustAtoi(str[daySep+1 : timeSep]) + + var hour, minute, second int + if len(str) > monSep+len("01-01")+1 { + expect(str, " ", timeSep) + minSep := timeSep + 3 + expect(str, ":", minSep) + hour = mustAtoi(str[timeSep+1 : minSep]) + secSep := minSep + 3 + expect(str, ":", secSep) + minute = mustAtoi(str[minSep+1 : secSep]) + secEnd := secSep + 3 + second = mustAtoi(str[secSep+1 : secEnd]) + } + remainderIdx := monSep + len("01-01 00:00:00") + 1 + // Three optional (but ordered) sections follow: the + // fractional seconds, the time zone offset, and the BC + // designation. We set them up here and adjust the other + // offsets if the preceding sections exist. + + nanoSec := 0 + tzOff := 0 + + if remainderIdx < len(str) && str[remainderIdx:remainderIdx+1] == "." { + fracStart := remainderIdx + 1 + fracOff := strings.IndexAny(str[fracStart:], "-+ ") + if fracOff < 0 { + fracOff = len(str) - fracStart + } + fracSec := mustAtoi(str[fracStart : fracStart+fracOff]) + nanoSec = fracSec * (1000000000 / int(math.Pow(10, float64(fracOff)))) + + remainderIdx += fracOff + 1 + } + if tzStart := remainderIdx; tzStart < len(str) && (str[tzStart:tzStart+1] == "-" || str[tzStart:tzStart+1] == "+") { + // time zone separator is always '-' or '+' (UTC is +00) + var tzSign int + if c := str[tzStart : tzStart+1]; c == "-" { + tzSign = -1 + } else if c == "+" { + tzSign = +1 + } else { + errorf("expected '-' or '+' at position %v; got %v", tzStart, c) + } + tzHours := mustAtoi(str[tzStart+1 : tzStart+3]) + remainderIdx += 3 + var tzMin, tzSec int + if tzStart+3 < len(str) && str[tzStart+3:tzStart+4] == ":" { + tzMin = mustAtoi(str[tzStart+4 : tzStart+6]) + remainderIdx += 3 + } + if tzStart+6 < len(str) && str[tzStart+6:tzStart+7] == ":" { + tzSec = mustAtoi(str[tzStart+7 : tzStart+9]) + remainderIdx += 3 + } + tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec) + } + var isoYear int + if remainderIdx < len(str) && str[remainderIdx:remainderIdx+3] == " BC" { + isoYear = 1 - year + remainderIdx += 3 + } else { + isoYear = year + } + if remainderIdx < len(str) { + errorf("expected end of input, got %v", str[remainderIdx:]) + } + t := time.Date(isoYear, time.Month(month), day, + hour, minute, second, nanoSec, + globalLocationCache.getLocation(tzOff)) + + if currentLocation != nil { + // Set the location of the returned Time based on the session's + // TimeZone value, but only if the local time zone database agrees with + // the remote database on the offset. + lt := t.In(currentLocation) + _, newOff := lt.Zone() + if newOff == tzOff { + t = lt + } + } + + return t +} + +// formatTs formats t into a format postgres understands. +func formatTs(t time.Time) (b []byte) { + if infinityTsEnabled { + // t <= -infinity : ! (t > -infinity) + if !t.After(infinityTsNegative) { + return []byte("-infinity") + } + // t >= infinity : ! (!t < infinity) + if !t.Before(infinityTsPositive) { + return []byte("infinity") + } + } + // Need to send dates before 0001 A.D. with " BC" suffix, instead of the + // minus sign preferred by Go. + // Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on + bc := false + if t.Year() <= 0 { + // flip year sign, and add 1, e.g: "0" will be "1", and "-10" will be "11" + t = t.AddDate((-t.Year())*2+1, 0, 0) + bc = true + } + b = []byte(t.Format(time.RFC3339Nano)) + + _, offset := t.Zone() + offset = offset % 60 + if offset != 0 { + // RFC3339Nano already printed the minus sign + if offset < 0 { + offset = -offset + } + + b = append(b, ':') + if offset < 10 { + b = append(b, '0') + } + b = strconv.AppendInt(b, int64(offset), 10) + } + + if bc { + b = append(b, " BC"...) + } + return b +} + +// Parse a bytea value received from the server. Both "hex" and the legacy +// "escape" format are supported. +func parseBytea(s []byte) (result []byte) { + if len(s) >= 2 && bytes.Equal(s[:2], []byte("\\x")) { + // bytea_output = hex + s = s[2:] // trim off leading "\\x" + result = make([]byte, hex.DecodedLen(len(s))) + _, err := hex.Decode(result, s) + if err != nil { + errorf("%s", err) + } + } else { + // bytea_output = escape + for len(s) > 0 { + if s[0] == '\\' { + // escaped '\\' + if len(s) >= 2 && s[1] == '\\' { + result = append(result, '\\') + s = s[2:] + continue + } + + // '\\' followed by an octal number + if len(s) < 4 { + errorf("invalid bytea sequence %v", s) + } + r, err := strconv.ParseInt(string(s[1:4]), 8, 9) + if err != nil { + errorf("could not parse bytea value: %s", err.Error()) + } + result = append(result, byte(r)) + s = s[4:] + } else { + // We hit an unescaped, raw byte. Try to read in as many as + // possible in one go. + i := bytes.IndexByte(s, '\\') + if i == -1 { + result = append(result, s...) + break + } + result = append(result, s[:i]...) + s = s[i:] + } + } + } + + return result +} + +func encodeBytea(serverVersion int, v []byte) (result []byte) { + if serverVersion >= 90000 { + // Use the hex format if we know that the server supports it + result = make([]byte, 2+hex.EncodedLen(len(v))) + result[0] = '\\' + result[1] = 'x' + hex.Encode(result[2:], v) + } else { + // .. or resort to "escape" + for _, b := range v { + if b == '\\' { + result = append(result, '\\', '\\') + } else if b < 0x20 || b > 0x7e { + result = append(result, []byte(fmt.Sprintf("\\%03o", b))...) + } else { + result = append(result, b) + } + } + } + + return result +} + +// NullTime represents a time.Time that may be null. NullTime implements the +// sql.Scanner interface so it can be used as a scan destination, similar to +// sql.NullString. +type NullTime struct { + Time time.Time + Valid bool // Valid is true if Time is not NULL +} + +// Scan implements the Scanner interface. +func (nt *NullTime) Scan(value interface{}) error { + nt.Time, nt.Valid = value.(time.Time) + return nil +} + +// Value implements the driver Valuer interface. +func (nt NullTime) Value() (driver.Value, error) { + if !nt.Valid { + return nil, nil + } + return nt.Time, nil +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/encode_test.go b/Godeps/_workspace/src/github.com/lib/pq/encode_test.go new file mode 100644 index 000000000..50fbaf33f --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/encode_test.go @@ -0,0 +1,570 @@ +package pq + +import ( + "bytes" + "fmt" + "testing" + "time" + + "github.com/lib/pq/oid" +) + +func TestScanTimestamp(t *testing.T) { + var nt NullTime + tn := time.Now() + nt.Scan(tn) + if !nt.Valid { + t.Errorf("Expected Valid=false") + } + if nt.Time != tn { + t.Errorf("Time value mismatch") + } +} + +func TestScanNilTimestamp(t *testing.T) { + var nt NullTime + nt.Scan(nil) + if nt.Valid { + t.Errorf("Expected Valid=false") + } +} + +var timeTests = []struct { + str string + timeval time.Time +}{ + {"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))}, + {"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.000001", time.Date(2001, time.February, 3, 4, 5, 6, 1000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.00001", time.Date(2001, time.February, 3, 4, 5, 6, 10000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.0001", time.Date(2001, time.February, 3, 4, 5, 6, 100000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.001", time.Date(2001, time.February, 3, 4, 5, 6, 1000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.01", time.Date(2001, time.February, 3, 4, 5, 6, 10000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.1", time.Date(2001, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.12", time.Date(2001, time.February, 3, 4, 5, 6, 120000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.123", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.1234", time.Date(2001, time.February, 3, 4, 5, 6, 123400000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.12345", time.Date(2001, time.February, 3, 4, 5, 6, 123450000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.123456", time.Date(2001, time.February, 3, 4, 5, 6, 123456000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.123-07", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, + time.FixedZone("", -7*60*60))}, + {"2001-02-03 04:05:06-07", time.Date(2001, time.February, 3, 4, 5, 6, 0, + time.FixedZone("", -7*60*60))}, + {"2001-02-03 04:05:06-07:42", time.Date(2001, time.February, 3, 4, 5, 6, 0, + time.FixedZone("", -(7*60*60+42*60)))}, + {"2001-02-03 04:05:06-07:30:09", time.Date(2001, time.February, 3, 4, 5, 6, 0, + time.FixedZone("", -(7*60*60+30*60+9)))}, + {"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0, + time.FixedZone("", 7*60*60))}, + {"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))}, + {"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, + time.FixedZone("", -7*60*60))}, + {"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"0001-02-03 04:05:06.123 BC", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)}, + {"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"0002-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)}, + {"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, + {"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, +} + +// Helper function for the two tests below +func tryParse(str string) (t time.Time, err error) { + defer func() { + if p := recover(); p != nil { + err = fmt.Errorf("%v", p) + return + } + }() + i := parseTs(nil, str) + t, ok := i.(time.Time) + if !ok { + err = fmt.Errorf("Not a time.Time type, got %#v", i) + } + return +} + +// Test that parsing the string results in the expected value. +func TestParseTs(t *testing.T) { + for i, tt := range timeTests { + val, err := tryParse(tt.str) + if err != nil { + t.Errorf("%d: got error: %v", i, err) + } else if val.String() != tt.timeval.String() { + t.Errorf("%d: expected to parse %q into %q; got %q", + i, tt.str, tt.timeval, val) + } + } +} + +// Now test that sending the value into the database and parsing it back +// returns the same time.Time value. +func TestEncodeAndParseTs(t *testing.T) { + db, err := openTestConnConninfo("timezone='Etc/UTC'") + if err != nil { + t.Fatal(err) + } + defer db.Close() + + for i, tt := range timeTests { + var dbstr string + err = db.QueryRow("SELECT ($1::timestamptz)::text", tt.timeval).Scan(&dbstr) + if err != nil { + t.Errorf("%d: could not send value %q to the database: %s", i, tt.timeval, err) + continue + } + + val, err := tryParse(dbstr) + if err != nil { + t.Errorf("%d: could not parse value %q: %s", i, dbstr, err) + continue + } + val = val.In(tt.timeval.Location()) + if val.String() != tt.timeval.String() { + t.Errorf("%d: expected to parse %q into %q; got %q", i, dbstr, tt.timeval, val) + } + } +} + +var formatTimeTests = []struct { + time time.Time + expected string +}{ + {time.Time{}, "0001-01-01T00:00:00Z"}, + {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03T04:05:06.123456789Z"}, + {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03T04:05:06.123456789+02:00"}, + {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03T04:05:06.123456789-06:00"}, + {time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03T04:05:06-07:30:09"}, + + {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z"}, + {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00"}, + {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00"}, + + {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z BC"}, + {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00 BC"}, + {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00 BC"}, + + {time.Date(1, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09"}, + {time.Date(0, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09 BC"}, +} + +func TestFormatTs(t *testing.T) { + for i, tt := range formatTimeTests { + val := string(formatTs(tt.time)) + if val != tt.expected { + t.Errorf("%d: incorrect time format %q, want %q", i, val, tt.expected) + } + } +} + +func TestTimestampWithTimeZone(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer tx.Rollback() + + // try several different locations, all included in Go's zoneinfo.zip + for _, locName := range []string{ + "UTC", + "America/Chicago", + "America/New_York", + "Australia/Darwin", + "Australia/Perth", + } { + loc, err := time.LoadLocation(locName) + if err != nil { + t.Logf("Could not load time zone %s - skipping", locName) + continue + } + + // Postgres timestamps have a resolution of 1 microsecond, so don't + // use the full range of the Nanosecond argument + refTime := time.Date(2012, 11, 6, 10, 23, 42, 123456000, loc) + + for _, pgTimeZone := range []string{"US/Eastern", "Australia/Darwin"} { + // Switch Postgres's timezone to test different output timestamp formats + _, err = tx.Exec(fmt.Sprintf("set time zone '%s'", pgTimeZone)) + if err != nil { + t.Fatal(err) + } + + var gotTime time.Time + row := tx.QueryRow("select $1::timestamp with time zone", refTime) + err = row.Scan(&gotTime) + if err != nil { + t.Fatal(err) + } + + if !refTime.Equal(gotTime) { + t.Errorf("timestamps not equal: %s != %s", refTime, gotTime) + } + + // check that the time zone is set correctly based on TimeZone + pgLoc, err := time.LoadLocation(pgTimeZone) + if err != nil { + t.Logf("Could not load time zone %s - skipping", pgLoc) + continue + } + translated := refTime.In(pgLoc) + if translated.String() != gotTime.String() { + t.Errorf("timestamps not equal: %s != %s", translated, gotTime) + } + } + } +} + +func TestTimestampWithOutTimezone(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + test := func(ts, pgts string) { + r, err := db.Query("SELECT $1::timestamp", pgts) + if err != nil { + t.Fatalf("Could not run query: %v", err) + } + + n := r.Next() + + if n != true { + t.Fatal("Expected at least one row") + } + + var result time.Time + err = r.Scan(&result) + if err != nil { + t.Fatalf("Did not expect error scanning row: %v", err) + } + + expected, err := time.Parse(time.RFC3339, ts) + if err != nil { + t.Fatalf("Could not parse test time literal: %v", err) + } + + if !result.Equal(expected) { + t.Fatalf("Expected time to match %v: got mismatch %v", + expected, result) + } + + n = r.Next() + if n != false { + t.Fatal("Expected only one row") + } + } + + test("2000-01-01T00:00:00Z", "2000-01-01T00:00:00") + + // Test higher precision time + test("2013-01-04T20:14:58.80033Z", "2013-01-04 20:14:58.80033") +} + +func TestInfinityTimestamp(t *testing.T) { + db := openTestConn(t) + defer db.Close() + var err error + var resultT time.Time + + expectedError := fmt.Errorf(`sql: Scan error on column index 0: unsupported driver -> Scan pair: []uint8 -> *time.Time`) + type testCases []struct { + Query string + Param string + ExpectedErr error + ExpectedVal interface{} + } + tc := testCases{ + {"SELECT $1::timestamp", "-infinity", expectedError, "-infinity"}, + {"SELECT $1::timestamptz", "-infinity", expectedError, "-infinity"}, + {"SELECT $1::timestamp", "infinity", expectedError, "infinity"}, + {"SELECT $1::timestamptz", "infinity", expectedError, "infinity"}, + } + // try to assert []byte to time.Time + for _, q := range tc { + err = db.QueryRow(q.Query, q.Param).Scan(&resultT) + if err.Error() != q.ExpectedErr.Error() { + t.Errorf("Scanning -/+infinity, expected error, %q, got %q", q.ExpectedErr, err) + } + } + // yield []byte + for _, q := range tc { + var resultI interface{} + err = db.QueryRow(q.Query, q.Param).Scan(&resultI) + if err != nil { + t.Errorf("Scanning -/+infinity, expected no error, got %q", err) + } + result, ok := resultI.([]byte) + if !ok { + t.Errorf("Scanning -/+infinity, expected []byte, got %#v", resultI) + } + if string(result) != q.ExpectedVal { + t.Errorf("Scanning -/+infinity, expected %q, got %q", q.ExpectedVal, result) + } + } + + y1500 := time.Date(1500, time.January, 1, 0, 0, 0, 0, time.UTC) + y2500 := time.Date(2500, time.January, 1, 0, 0, 0, 0, time.UTC) + EnableInfinityTs(y1500, y2500) + + err = db.QueryRow("SELECT $1::timestamp", "infinity").Scan(&resultT) + if err != nil { + t.Errorf("Scanning infinity, expected no error, got %q", err) + } + if !resultT.Equal(y2500) { + t.Errorf("Scanning infinity, expected %q, got %q", y2500, resultT) + } + + err = db.QueryRow("SELECT $1::timestamptz", "infinity").Scan(&resultT) + if err != nil { + t.Errorf("Scanning infinity, expected no error, got %q", err) + } + if !resultT.Equal(y2500) { + t.Errorf("Scanning Infinity, expected time %q, got %q", y2500, resultT.String()) + } + + err = db.QueryRow("SELECT $1::timestamp", "-infinity").Scan(&resultT) + if err != nil { + t.Errorf("Scanning -infinity, expected no error, got %q", err) + } + if !resultT.Equal(y1500) { + t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String()) + } + + err = db.QueryRow("SELECT $1::timestamptz", "-infinity").Scan(&resultT) + if err != nil { + t.Errorf("Scanning -infinity, expected no error, got %q", err) + } + if !resultT.Equal(y1500) { + t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String()) + } + + y_1500 := time.Date(-1500, time.January, 1, 0, 0, 0, 0, time.UTC) + y11500 := time.Date(11500, time.January, 1, 0, 0, 0, 0, time.UTC) + var s string + err = db.QueryRow("SELECT $1::timestamp::text", y_1500).Scan(&s) + if err != nil { + t.Errorf("Encoding -infinity, expected no error, got %q", err) + } + if s != "-infinity" { + t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s) + } + err = db.QueryRow("SELECT $1::timestamptz::text", y_1500).Scan(&s) + if err != nil { + t.Errorf("Encoding -infinity, expected no error, got %q", err) + } + if s != "-infinity" { + t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s) + } + + err = db.QueryRow("SELECT $1::timestamp::text", y11500).Scan(&s) + if err != nil { + t.Errorf("Encoding infinity, expected no error, got %q", err) + } + if s != "infinity" { + t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s) + } + err = db.QueryRow("SELECT $1::timestamptz::text", y11500).Scan(&s) + if err != nil { + t.Errorf("Encoding infinity, expected no error, got %q", err) + } + if s != "infinity" { + t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s) + } + + disableInfinityTs() + + var panicErrorString string + func() { + defer func() { + panicErrorString, _ = recover().(string) + }() + EnableInfinityTs(y2500, y1500) + }() + if panicErrorString != infinityTsNegativeMustBeSmaller { + t.Errorf("Expected error, %q, got %q", infinityTsNegativeMustBeSmaller, panicErrorString) + } +} + +func TestStringWithNul(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + hello0world := string("hello\x00world") + _, err := db.Query("SELECT $1::text", &hello0world) + if err == nil { + t.Fatal("Postgres accepts a string with nul in it; " + + "injection attacks may be plausible") + } +} + +func TestByteaToText(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + b := []byte("hello world") + row := db.QueryRow("SELECT $1::text", b) + + var result []byte + err := row.Scan(&result) + if err != nil { + t.Fatal(err) + } + + if string(result) != string(b) { + t.Fatalf("expected %v but got %v", b, result) + } +} + +func TestTextToBytea(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + b := "hello world" + row := db.QueryRow("SELECT $1::bytea", b) + + var result []byte + err := row.Scan(&result) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(result, []byte(b)) { + t.Fatalf("expected %v but got %v", b, result) + } +} + +func TestByteaOutputFormatEncoding(t *testing.T) { + input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123") + want := []byte("\\x5c78000102fffe6162636465666730313233") + got := encode(¶meterStatus{serverVersion: 90000}, input, oid.T_bytea) + if !bytes.Equal(want, got) { + t.Errorf("invalid hex bytea output, got %v but expected %v", got, want) + } + + want = []byte("\\\\x\\000\\001\\002\\377\\376abcdefg0123") + got = encode(¶meterStatus{serverVersion: 84000}, input, oid.T_bytea) + if !bytes.Equal(want, got) { + t.Errorf("invalid escape bytea output, got %v but expected %v", got, want) + } +} + +func TestByteaOutputFormats(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + if getServerVersion(t, db) < 90000 { + // skip + return + } + + testByteaOutputFormat := func(f string) { + expectedData := []byte("\x5c\x78\x00\xff\x61\x62\x63\x01\x08") + sqlQuery := "SELECT decode('5c7800ff6162630108', 'hex')" + + var data []byte + + // use a txn to avoid relying on getting the same connection + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("SET LOCAL bytea_output TO " + f) + if err != nil { + t.Fatal(err) + } + // use Query; QueryRow would hide the actual error + rows, err := txn.Query(sqlQuery) + if err != nil { + t.Fatal(err) + } + if !rows.Next() { + if rows.Err() != nil { + t.Fatal(rows.Err()) + } + t.Fatal("shouldn't happen") + } + err = rows.Scan(&data) + if err != nil { + t.Fatal(err) + } + err = rows.Close() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(data, expectedData) { + t.Errorf("unexpected bytea value %v for format %s; expected %v", data, f, expectedData) + } + } + + testByteaOutputFormat("hex") + testByteaOutputFormat("escape") +} + +func TestAppendEncodedText(t *testing.T) { + var buf []byte + + buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, int64(10)) + buf = append(buf, '\t') + buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, 42.0000000001) + buf = append(buf, '\t') + buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, "hello\tworld") + buf = append(buf, '\t') + buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, []byte{0, 128, 255}) + + if string(buf) != "10\t42.0000000001\thello\\tworld\t\\\\x0080ff" { + t.Fatal(string(buf)) + } +} + +func TestAppendEscapedText(t *testing.T) { + if esc := appendEscapedText(nil, "hallo\tescape"); string(esc) != "hallo\\tescape" { + t.Fatal(string(esc)) + } + if esc := appendEscapedText(nil, "hallo\\tescape\n"); string(esc) != "hallo\\\\tescape\\n" { + t.Fatal(string(esc)) + } + if esc := appendEscapedText(nil, "\n\r\t\f"); string(esc) != "\\n\\r\\t\f" { + t.Fatal(string(esc)) + } +} + +func TestAppendEscapedTextExistingBuffer(t *testing.T) { + var buf []byte + buf = []byte("123\t") + if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" { + t.Fatal(string(esc)) + } + buf = []byte("123\t") + if esc := appendEscapedText(buf, "hallo\\tescape\n"); string(esc) != "123\thallo\\\\tescape\\n" { + t.Fatal(string(esc)) + } + buf = []byte("123\t") + if esc := appendEscapedText(buf, "\n\r\t\f"); string(esc) != "123\t\\n\\r\\t\f" { + t.Fatal(string(esc)) + } +} + +func BenchmarkAppendEscapedText(b *testing.B) { + longString := "" + for i := 0; i < 100; i++ { + longString += "123456789\n" + } + for i := 0; i < b.N; i++ { + appendEscapedText(nil, longString) + } +} + +func BenchmarkAppendEscapedTextNoEscape(b *testing.B) { + longString := "" + for i := 0; i < 100; i++ { + longString += "1234567890" + } + for i := 0; i < b.N; i++ { + appendEscapedText(nil, longString) + } +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/error.go b/Godeps/_workspace/src/github.com/lib/pq/error.go new file mode 100644 index 000000000..b4bb44cee --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/error.go @@ -0,0 +1,508 @@ +package pq + +import ( + "database/sql/driver" + "fmt" + "io" + "net" + "runtime" +) + +// Error severities +const ( + Efatal = "FATAL" + Epanic = "PANIC" + Ewarning = "WARNING" + Enotice = "NOTICE" + Edebug = "DEBUG" + Einfo = "INFO" + Elog = "LOG" +) + +// Error represents an error communicating with the server. +// +// See http://www.postgresql.org/docs/current/static/protocol-error-fields.html for details of the fields +type Error struct { + Severity string + Code ErrorCode + Message string + Detail string + Hint string + Position string + InternalPosition string + InternalQuery string + Where string + Schema string + Table string + Column string + DataTypeName string + Constraint string + File string + Line string + Routine string +} + +// ErrorCode is a five-character error code. +type ErrorCode string + +// Name returns a more human friendly rendering of the error code, namely the +// "condition name". +// +// See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for +// details. +func (ec ErrorCode) Name() string { + return errorCodeNames[ec] +} + +// ErrorClass is only the class part of an error code. +type ErrorClass string + +// Name returns the condition name of an error class. It is equivalent to the +// condition name of the "standard" error code (i.e. the one having the last +// three characters "000"). +func (ec ErrorClass) Name() string { + return errorCodeNames[ErrorCode(ec+"000")] +} + +// Class returns the error class, e.g. "28". +// +// See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for +// details. +func (ec ErrorCode) Class() ErrorClass { + return ErrorClass(ec[0:2]) +} + +// errorCodeNames is a mapping between the five-character error codes and the +// human readable "condition names". It is derived from the list at +// http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html +var errorCodeNames = map[ErrorCode]string{ + // Class 00 - Successful Completion + "00000": "successful_completion", + // Class 01 - Warning + "01000": "warning", + "0100C": "dynamic_result_sets_returned", + "01008": "implicit_zero_bit_padding", + "01003": "null_value_eliminated_in_set_function", + "01007": "privilege_not_granted", + "01006": "privilege_not_revoked", + "01004": "string_data_right_truncation", + "01P01": "deprecated_feature", + // Class 02 - No Data (this is also a warning class per the SQL standard) + "02000": "no_data", + "02001": "no_additional_dynamic_result_sets_returned", + // Class 03 - SQL Statement Not Yet Complete + "03000": "sql_statement_not_yet_complete", + // Class 08 - Connection Exception + "08000": "connection_exception", + "08003": "connection_does_not_exist", + "08006": "connection_failure", + "08001": "sqlclient_unable_to_establish_sqlconnection", + "08004": "sqlserver_rejected_establishment_of_sqlconnection", + "08007": "transaction_resolution_unknown", + "08P01": "protocol_violation", + // Class 09 - Triggered Action Exception + "09000": "triggered_action_exception", + // Class 0A - Feature Not Supported + "0A000": "feature_not_supported", + // Class 0B - Invalid Transaction Initiation + "0B000": "invalid_transaction_initiation", + // Class 0F - Locator Exception + "0F000": "locator_exception", + "0F001": "invalid_locator_specification", + // Class 0L - Invalid Grantor + "0L000": "invalid_grantor", + "0LP01": "invalid_grant_operation", + // Class 0P - Invalid Role Specification + "0P000": "invalid_role_specification", + // Class 0Z - Diagnostics Exception + "0Z000": "diagnostics_exception", + "0Z002": "stacked_diagnostics_accessed_without_active_handler", + // Class 20 - Case Not Found + "20000": "case_not_found", + // Class 21 - Cardinality Violation + "21000": "cardinality_violation", + // Class 22 - Data Exception + "22000": "data_exception", + "2202E": "array_subscript_error", + "22021": "character_not_in_repertoire", + "22008": "datetime_field_overflow", + "22012": "division_by_zero", + "22005": "error_in_assignment", + "2200B": "escape_character_conflict", + "22022": "indicator_overflow", + "22015": "interval_field_overflow", + "2201E": "invalid_argument_for_logarithm", + "22014": "invalid_argument_for_ntile_function", + "22016": "invalid_argument_for_nth_value_function", + "2201F": "invalid_argument_for_power_function", + "2201G": "invalid_argument_for_width_bucket_function", + "22018": "invalid_character_value_for_cast", + "22007": "invalid_datetime_format", + "22019": "invalid_escape_character", + "2200D": "invalid_escape_octet", + "22025": "invalid_escape_sequence", + "22P06": "nonstandard_use_of_escape_character", + "22010": "invalid_indicator_parameter_value", + "22023": "invalid_parameter_value", + "2201B": "invalid_regular_expression", + "2201W": "invalid_row_count_in_limit_clause", + "2201X": "invalid_row_count_in_result_offset_clause", + "22009": "invalid_time_zone_displacement_value", + "2200C": "invalid_use_of_escape_character", + "2200G": "most_specific_type_mismatch", + "22004": "null_value_not_allowed", + "22002": "null_value_no_indicator_parameter", + "22003": "numeric_value_out_of_range", + "22026": "string_data_length_mismatch", + "22001": "string_data_right_truncation", + "22011": "substring_error", + "22027": "trim_error", + "22024": "unterminated_c_string", + "2200F": "zero_length_character_string", + "22P01": "floating_point_exception", + "22P02": "invalid_text_representation", + "22P03": "invalid_binary_representation", + "22P04": "bad_copy_file_format", + "22P05": "untranslatable_character", + "2200L": "not_an_xml_document", + "2200M": "invalid_xml_document", + "2200N": "invalid_xml_content", + "2200S": "invalid_xml_comment", + "2200T": "invalid_xml_processing_instruction", + // Class 23 - Integrity Constraint Violation + "23000": "integrity_constraint_violation", + "23001": "restrict_violation", + "23502": "not_null_violation", + "23503": "foreign_key_violation", + "23505": "unique_violation", + "23514": "check_violation", + "23P01": "exclusion_violation", + // Class 24 - Invalid Cursor State + "24000": "invalid_cursor_state", + // Class 25 - Invalid Transaction State + "25000": "invalid_transaction_state", + "25001": "active_sql_transaction", + "25002": "branch_transaction_already_active", + "25008": "held_cursor_requires_same_isolation_level", + "25003": "inappropriate_access_mode_for_branch_transaction", + "25004": "inappropriate_isolation_level_for_branch_transaction", + "25005": "no_active_sql_transaction_for_branch_transaction", + "25006": "read_only_sql_transaction", + "25007": "schema_and_data_statement_mixing_not_supported", + "25P01": "no_active_sql_transaction", + "25P02": "in_failed_sql_transaction", + // Class 26 - Invalid SQL Statement Name + "26000": "invalid_sql_statement_name", + // Class 27 - Triggered Data Change Violation + "27000": "triggered_data_change_violation", + // Class 28 - Invalid Authorization Specification + "28000": "invalid_authorization_specification", + "28P01": "invalid_password", + // Class 2B - Dependent Privilege Descriptors Still Exist + "2B000": "dependent_privilege_descriptors_still_exist", + "2BP01": "dependent_objects_still_exist", + // Class 2D - Invalid Transaction Termination + "2D000": "invalid_transaction_termination", + // Class 2F - SQL Routine Exception + "2F000": "sql_routine_exception", + "2F005": "function_executed_no_return_statement", + "2F002": "modifying_sql_data_not_permitted", + "2F003": "prohibited_sql_statement_attempted", + "2F004": "reading_sql_data_not_permitted", + // Class 34 - Invalid Cursor Name + "34000": "invalid_cursor_name", + // Class 38 - External Routine Exception + "38000": "external_routine_exception", + "38001": "containing_sql_not_permitted", + "38002": "modifying_sql_data_not_permitted", + "38003": "prohibited_sql_statement_attempted", + "38004": "reading_sql_data_not_permitted", + // Class 39 - External Routine Invocation Exception + "39000": "external_routine_invocation_exception", + "39001": "invalid_sqlstate_returned", + "39004": "null_value_not_allowed", + "39P01": "trigger_protocol_violated", + "39P02": "srf_protocol_violated", + // Class 3B - Savepoint Exception + "3B000": "savepoint_exception", + "3B001": "invalid_savepoint_specification", + // Class 3D - Invalid Catalog Name + "3D000": "invalid_catalog_name", + // Class 3F - Invalid Schema Name + "3F000": "invalid_schema_name", + // Class 40 - Transaction Rollback + "40000": "transaction_rollback", + "40002": "transaction_integrity_constraint_violation", + "40001": "serialization_failure", + "40003": "statement_completion_unknown", + "40P01": "deadlock_detected", + // Class 42 - Syntax Error or Access Rule Violation + "42000": "syntax_error_or_access_rule_violation", + "42601": "syntax_error", + "42501": "insufficient_privilege", + "42846": "cannot_coerce", + "42803": "grouping_error", + "42P20": "windowing_error", + "42P19": "invalid_recursion", + "42830": "invalid_foreign_key", + "42602": "invalid_name", + "42622": "name_too_long", + "42939": "reserved_name", + "42804": "datatype_mismatch", + "42P18": "indeterminate_datatype", + "42P21": "collation_mismatch", + "42P22": "indeterminate_collation", + "42809": "wrong_object_type", + "42703": "undefined_column", + "42883": "undefined_function", + "42P01": "undefined_table", + "42P02": "undefined_parameter", + "42704": "undefined_object", + "42701": "duplicate_column", + "42P03": "duplicate_cursor", + "42P04": "duplicate_database", + "42723": "duplicate_function", + "42P05": "duplicate_prepared_statement", + "42P06": "duplicate_schema", + "42P07": "duplicate_table", + "42712": "duplicate_alias", + "42710": "duplicate_object", + "42702": "ambiguous_column", + "42725": "ambiguous_function", + "42P08": "ambiguous_parameter", + "42P09": "ambiguous_alias", + "42P10": "invalid_column_reference", + "42611": "invalid_column_definition", + "42P11": "invalid_cursor_definition", + "42P12": "invalid_database_definition", + "42P13": "invalid_function_definition", + "42P14": "invalid_prepared_statement_definition", + "42P15": "invalid_schema_definition", + "42P16": "invalid_table_definition", + "42P17": "invalid_object_definition", + // Class 44 - WITH CHECK OPTION Violation + "44000": "with_check_option_violation", + // Class 53 - Insufficient Resources + "53000": "insufficient_resources", + "53100": "disk_full", + "53200": "out_of_memory", + "53300": "too_many_connections", + "53400": "configuration_limit_exceeded", + // Class 54 - Program Limit Exceeded + "54000": "program_limit_exceeded", + "54001": "statement_too_complex", + "54011": "too_many_columns", + "54023": "too_many_arguments", + // Class 55 - Object Not In Prerequisite State + "55000": "object_not_in_prerequisite_state", + "55006": "object_in_use", + "55P02": "cant_change_runtime_param", + "55P03": "lock_not_available", + // Class 57 - Operator Intervention + "57000": "operator_intervention", + "57014": "query_canceled", + "57P01": "admin_shutdown", + "57P02": "crash_shutdown", + "57P03": "cannot_connect_now", + "57P04": "database_dropped", + // Class 58 - System Error (errors external to PostgreSQL itself) + "58000": "system_error", + "58030": "io_error", + "58P01": "undefined_file", + "58P02": "duplicate_file", + // Class F0 - Configuration File Error + "F0000": "config_file_error", + "F0001": "lock_file_exists", + // Class HV - Foreign Data Wrapper Error (SQL/MED) + "HV000": "fdw_error", + "HV005": "fdw_column_name_not_found", + "HV002": "fdw_dynamic_parameter_value_needed", + "HV010": "fdw_function_sequence_error", + "HV021": "fdw_inconsistent_descriptor_information", + "HV024": "fdw_invalid_attribute_value", + "HV007": "fdw_invalid_column_name", + "HV008": "fdw_invalid_column_number", + "HV004": "fdw_invalid_data_type", + "HV006": "fdw_invalid_data_type_descriptors", + "HV091": "fdw_invalid_descriptor_field_identifier", + "HV00B": "fdw_invalid_handle", + "HV00C": "fdw_invalid_option_index", + "HV00D": "fdw_invalid_option_name", + "HV090": "fdw_invalid_string_length_or_buffer_length", + "HV00A": "fdw_invalid_string_format", + "HV009": "fdw_invalid_use_of_null_pointer", + "HV014": "fdw_too_many_handles", + "HV001": "fdw_out_of_memory", + "HV00P": "fdw_no_schemas", + "HV00J": "fdw_option_name_not_found", + "HV00K": "fdw_reply_handle", + "HV00Q": "fdw_schema_not_found", + "HV00R": "fdw_table_not_found", + "HV00L": "fdw_unable_to_create_execution", + "HV00M": "fdw_unable_to_create_reply", + "HV00N": "fdw_unable_to_establish_connection", + // Class P0 - PL/pgSQL Error + "P0000": "plpgsql_error", + "P0001": "raise_exception", + "P0002": "no_data_found", + "P0003": "too_many_rows", + // Class XX - Internal Error + "XX000": "internal_error", + "XX001": "data_corrupted", + "XX002": "index_corrupted", +} + +func parseError(r *readBuf) *Error { + err := new(Error) + for t := r.byte(); t != 0; t = r.byte() { + msg := r.string() + switch t { + case 'S': + err.Severity = msg + case 'C': + err.Code = ErrorCode(msg) + case 'M': + err.Message = msg + case 'D': + err.Detail = msg + case 'H': + err.Hint = msg + case 'P': + err.Position = msg + case 'p': + err.InternalPosition = msg + case 'q': + err.InternalQuery = msg + case 'W': + err.Where = msg + case 's': + err.Schema = msg + case 't': + err.Table = msg + case 'c': + err.Column = msg + case 'd': + err.DataTypeName = msg + case 'n': + err.Constraint = msg + case 'F': + err.File = msg + case 'L': + err.Line = msg + case 'R': + err.Routine = msg + } + } + return err +} + +// Fatal returns true if the Error Severity is fatal. +func (err *Error) Fatal() bool { + return err.Severity == Efatal +} + +// Get implements the legacy PGError interface. New code should use the fields +// of the Error struct directly. +func (err *Error) Get(k byte) (v string) { + switch k { + case 'S': + return err.Severity + case 'C': + return string(err.Code) + case 'M': + return err.Message + case 'D': + return err.Detail + case 'H': + return err.Hint + case 'P': + return err.Position + case 'p': + return err.InternalPosition + case 'q': + return err.InternalQuery + case 'W': + return err.Where + case 's': + return err.Schema + case 't': + return err.Table + case 'c': + return err.Column + case 'd': + return err.DataTypeName + case 'n': + return err.Constraint + case 'F': + return err.File + case 'L': + return err.Line + case 'R': + return err.Routine + } + return "" +} + +func (err Error) Error() string { + return "pq: " + err.Message +} + +// PGError is an interface used by previous versions of pq. It is provided +// only to support legacy code. New code should use the Error type. +type PGError interface { + Error() string + Fatal() bool + Get(k byte) (v string) +} + +func errorf(s string, args ...interface{}) { + panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))) +} + +func errRecoverNoErrBadConn(err *error) { + e := recover() + if e == nil { + // Do nothing + return + } + var ok bool + *err, ok = e.(error) + if !ok { + *err = fmt.Errorf("pq: unexpected error: %#v", e) + } +} + +func (c *conn) errRecover(err *error) { + e := recover() + switch v := e.(type) { + case nil: + // Do nothing + case runtime.Error: + c.bad = true + panic(v) + case *Error: + if v.Fatal() { + *err = driver.ErrBadConn + } else { + *err = v + } + case *net.OpError: + *err = driver.ErrBadConn + case error: + if v == io.EOF || v.(error).Error() == "remote error: handshake failure" { + *err = driver.ErrBadConn + } else { + *err = v + } + + default: + c.bad = true + panic(fmt.Sprintf("unknown error: %#v", e)) + } + + // Any time we return ErrBadConn, we need to remember it since *Tx doesn't + // mark the connection bad in database/sql. + if *err == driver.ErrBadConn { + c.bad = true + } +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore.go b/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore.go new file mode 100644 index 000000000..72d5abf51 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore.go @@ -0,0 +1,118 @@ +package hstore + +import ( + "database/sql" + "database/sql/driver" + "strings" +) + +// A wrapper for transferring Hstore values back and forth easily. +type Hstore struct { + Map map[string]sql.NullString +} + +// escapes and quotes hstore keys/values +// s should be a sql.NullString or string +func hQuote(s interface{}) string { + var str string + switch v := s.(type) { + case sql.NullString: + if !v.Valid { + return "NULL" + } + str = v.String + case string: + str = v + default: + panic("not a string or sql.NullString") + } + + str = strings.Replace(str, "\\", "\\\\", -1) + return `"` + strings.Replace(str, "\"", "\\\"", -1) + `"` +} + +// Scan implements the Scanner interface. +// +// Note h.Map is reallocated before the scan to clear existing values. If the +// hstore column's database value is NULL, then h.Map is set to nil instead. +func (h *Hstore) Scan(value interface{}) error { + if value == nil { + h.Map = nil + return nil + } + h.Map = make(map[string]sql.NullString) + var b byte + pair := [][]byte{{}, {}} + pi := 0 + inQuote := false + didQuote := false + sawSlash := false + bindex := 0 + for bindex, b = range value.([]byte) { + if sawSlash { + pair[pi] = append(pair[pi], b) + sawSlash = false + continue + } + + switch b { + case '\\': + sawSlash = true + continue + case '"': + inQuote = !inQuote + if !didQuote { + didQuote = true + } + continue + default: + if !inQuote { + switch b { + case ' ', '\t', '\n', '\r': + continue + case '=': + continue + case '>': + pi = 1 + didQuote = false + continue + case ',': + s := string(pair[1]) + if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { + h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} + } else { + h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} + } + pair[0] = []byte{} + pair[1] = []byte{} + pi = 0 + continue + } + } + } + pair[pi] = append(pair[pi], b) + } + if bindex > 0 { + s := string(pair[1]) + if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { + h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} + } else { + h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} + } + } + return nil +} + +// Value implements the driver Valuer interface. Note if h.Map is nil, the +// database column value will be set to NULL. +func (h Hstore) Value() (driver.Value, error) { + if h.Map == nil { + return nil, nil + } + parts := []string{} + for key, val := range h.Map { + thispart := hQuote(key) + "=>" + hQuote(val) + parts = append(parts, thispart) + } + return []byte(strings.Join(parts, ",")), nil +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore_test.go b/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore_test.go new file mode 100644 index 000000000..c9c108fc3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore_test.go @@ -0,0 +1,148 @@ +package hstore + +import ( + "database/sql" + "os" + "testing" + + _ "github.com/lib/pq" +) + +type Fatalistic interface { + Fatal(args ...interface{}) +} + +func openTestConn(t Fatalistic) *sql.DB { + datname := os.Getenv("PGDATABASE") + sslmode := os.Getenv("PGSSLMODE") + + if datname == "" { + os.Setenv("PGDATABASE", "pqgotest") + } + + if sslmode == "" { + os.Setenv("PGSSLMODE", "disable") + } + + conn, err := sql.Open("postgres", "") + if err != nil { + t.Fatal(err) + } + + return conn +} + +func TestHstore(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + // quitely create hstore if it doesn't exist + _, err := db.Exec("CREATE EXTENSION IF NOT EXISTS hstore") + if err != nil { + t.Skipf("Skipping hstore tests - hstore extension create failed: %s", err.Error()) + } + + hs := Hstore{} + + // test for null-valued hstores + err = db.QueryRow("SELECT NULL::hstore").Scan(&hs) + if err != nil { + t.Fatal(err) + } + if hs.Map != nil { + t.Fatalf("expected null map") + } + + err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs) + if err != nil { + t.Fatalf("re-query null map failed: %s", err.Error()) + } + if hs.Map != nil { + t.Fatalf("expected null map") + } + + // test for empty hstores + err = db.QueryRow("SELECT ''::hstore").Scan(&hs) + if err != nil { + t.Fatal(err) + } + if hs.Map == nil { + t.Fatalf("expected empty map, got null map") + } + if len(hs.Map) != 0 { + t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map)) + } + + err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs) + if err != nil { + t.Fatalf("re-query empty map failed: %s", err.Error()) + } + if hs.Map == nil { + t.Fatalf("expected empty map, got null map") + } + if len(hs.Map) != 0 { + t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map)) + } + + // a few example maps to test out + hsOnePair := Hstore{ + Map: map[string]sql.NullString{ + "key1": {"value1", true}, + }, + } + + hsThreePairs := Hstore{ + Map: map[string]sql.NullString{ + "key1": {"value1", true}, + "key2": {"value2", true}, + "key3": {"value3", true}, + }, + } + + hsSmorgasbord := Hstore{ + Map: map[string]sql.NullString{ + "nullstring": {"NULL", true}, + "actuallynull": {"", false}, + "NULL": {"NULL string key", true}, + "withbracket": {"value>42", true}, + "withequal": {"value=42", true}, + `"withquotes1"`: {`this "should" be fine`, true}, + `"withquotes"2"`: {`this "should\" also be fine`, true}, + "embedded1": {"value1=>x1", true}, + "embedded2": {`"value2"=>x2`, true}, + "withnewlines": {"\n\nvalue\t=>2", true}, + "<>": {`this, "should,\" also, => be fine`, true}, + }, + } + + // test encoding in query params, then decoding during Scan + testBidirectional := func(h Hstore) { + err = db.QueryRow("SELECT $1::hstore", h).Scan(&hs) + if err != nil { + t.Fatalf("re-query %d-pair map failed: %s", len(h.Map), err.Error()) + } + if hs.Map == nil { + t.Fatalf("expected %d-pair map, got null map", len(h.Map)) + } + if len(hs.Map) != len(h.Map) { + t.Fatalf("expected %d-pair map, got len(map)=%d", len(h.Map), len(hs.Map)) + } + + for key, val := range hs.Map { + otherval, found := h.Map[key] + if !found { + t.Fatalf(" key '%v' not found in %d-pair map", key, len(h.Map)) + } + if otherval.Valid != val.Valid { + t.Fatalf(" value %v <> %v in %d-pair map", otherval, val, len(h.Map)) + } + if otherval.String != val.String { + t.Fatalf(" value '%v' <> '%v' in %d-pair map", otherval.String, val.String, len(h.Map)) + } + } + } + + testBidirectional(hsOnePair) + testBidirectional(hsThreePairs) + testBidirectional(hsSmorgasbord) +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/listen_example/doc.go b/Godeps/_workspace/src/github.com/lib/pq/listen_example/doc.go new file mode 100644 index 000000000..5bc99f5c1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/listen_example/doc.go @@ -0,0 +1,102 @@ +/* + +Below you will find a self-contained Go program which uses the LISTEN / NOTIFY +mechanism to avoid polling the database while waiting for more work to arrive. + + // + // You can see the program in action by defining a function similar to + // the following: + // + // CREATE OR REPLACE FUNCTION public.get_work() + // RETURNS bigint + // LANGUAGE sql + // AS $$ + // SELECT CASE WHEN random() >= 0.2 THEN int8 '1' END + // $$ + // ; + + package main + + import ( + "database/sql" + "fmt" + "time" + + "github.com/lib/pq" + ) + + func doWork(db *sql.DB, work int64) { + // work here + } + + func getWork(db *sql.DB) { + for { + // get work from the database here + var work sql.NullInt64 + err := db.QueryRow("SELECT get_work()").Scan(&work) + if err != nil { + fmt.Println("call to get_work() failed: ", err) + time.Sleep(10 * time.Second) + continue + } + if !work.Valid { + // no more work to do + fmt.Println("ran out of work") + return + } + + fmt.Println("starting work on ", work.Int64) + go doWork(db, work.Int64) + } + } + + func waitForNotification(l *pq.Listener) { + for { + select { + case <-l.Notify: + fmt.Println("received notification, new work available") + return + case <-time.After(90 * time.Second): + go func() { + l.Ping() + }() + // Check if there's more work available, just in case it takes + // a while for the Listener to notice connection loss and + // reconnect. + fmt.Println("received no work for 90 seconds, checking for new work") + return + } + } + } + + func main() { + var conninfo string = "" + + db, err := sql.Open("postgres", conninfo) + if err != nil { + panic(err) + } + + reportProblem := func(ev pq.ListenerEventType, err error) { + if err != nil { + fmt.Println(err.Error()) + } + } + + listener := pq.NewListener(conninfo, 10 * time.Second, time.Minute, reportProblem) + err = listener.Listen("getwork") + if err != nil { + panic(err) + } + + fmt.Println("entering main loop") + for { + // process all available work before waiting for notifications + getWork(db) + waitForNotification(listener) + } + } + + +*/ +package listen_example diff --git a/Godeps/_workspace/src/github.com/lib/pq/notify.go b/Godeps/_workspace/src/github.com/lib/pq/notify.go new file mode 100644 index 000000000..c756af8b9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/notify.go @@ -0,0 +1,764 @@ +package pq + +// Package pq is a pure Go Postgres driver for the database/sql package. +// This module contains support for Postgres LISTEN/NOTIFY. + +import ( + "errors" + "fmt" + "sync" + "sync/atomic" + "time" +) + +// Notification represents a single notification from the database. +type Notification struct { + // Process ID (PID) of the notifying postgres backend. + BePid int + // Name of the channel the notification was sent on. + Channel string + // Payload, or the empty string if unspecified. + Extra string +} + +func recvNotification(r *readBuf) *Notification { + bePid := r.int32() + channel := r.string() + extra := r.string() + + return &Notification{bePid, channel, extra} +} + +const ( + connStateIdle int32 = iota + connStateExpectResponse + connStateExpectReadyForQuery +) + +type message struct { + typ byte + err error +} + +var errListenerConnClosed = errors.New("pq: ListenerConn has been closed") + +// ListenerConn is a low-level interface for waiting for notifications. You +// should use Listener instead. +type ListenerConn struct { + // guards cn and err + connectionLock sync.Mutex + cn *conn + err error + + connState int32 + + // the sending goroutine will be holding this lock + senderLock sync.Mutex + + notificationChan chan<- *Notification + + replyChan chan message +} + +// Creates a new ListenerConn. Use NewListener instead. +func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) { + cn, err := Open(name) + if err != nil { + return nil, err + } + + l := &ListenerConn{ + cn: cn.(*conn), + notificationChan: notificationChan, + connState: connStateIdle, + replyChan: make(chan message, 2), + } + + go l.listenerConnMain() + + return l, nil +} + +// We can only allow one goroutine at a time to be running a query on the +// connection for various reasons, so the goroutine sending on the connection +// must be holding senderLock. +// +// Returns an error if an unrecoverable error has occurred and the ListenerConn +// should be abandoned. +func (l *ListenerConn) acquireSenderLock() error { + // we must acquire senderLock first to avoid deadlocks; see ExecSimpleQuery + l.senderLock.Lock() + + l.connectionLock.Lock() + err := l.err + l.connectionLock.Unlock() + if err != nil { + l.senderLock.Unlock() + return err + } + return nil +} + +func (l *ListenerConn) releaseSenderLock() { + l.senderLock.Unlock() +} + +// setState advances the protocol state to newState. Returns false if moving +// to that state from the current state is not allowed. +func (l *ListenerConn) setState(newState int32) bool { + var expectedState int32 + + switch newState { + case connStateIdle: + expectedState = connStateExpectReadyForQuery + case connStateExpectResponse: + expectedState = connStateIdle + case connStateExpectReadyForQuery: + expectedState = connStateExpectResponse + default: + panic(fmt.Sprintf("unexpected listenerConnState %d", newState)) + } + + return atomic.CompareAndSwapInt32(&l.connState, expectedState, newState) +} + +// Main logic is here: receive messages from the postgres backend, forward +// notifications and query replies and keep the internal state in sync with the +// protocol state. Returns when the connection has been lost, is about to go +// away or should be discarded because we couldn't agree on the state with the +// server backend. +func (l *ListenerConn) listenerConnLoop() (err error) { + defer errRecoverNoErrBadConn(&err) + + r := &readBuf{} + for { + t, err := l.cn.recvMessage(r) + if err != nil { + return err + } + + switch t { + case 'A': + // recvNotification copies all the data so we don't need to worry + // about the scratch buffer being overwritten. + l.notificationChan <- recvNotification(r) + + case 'T', 'D': + // only used by tests; ignore + + case 'E': + // We might receive an ErrorResponse even when not in a query; it + // is expected that the server will close the connection after + // that, but we should make sure that the error we display is the + // one from the stray ErrorResponse, not io.ErrUnexpectedEOF. + if !l.setState(connStateExpectReadyForQuery) { + return parseError(r) + } + l.replyChan <- message{t, parseError(r)} + + case 'C', 'I': + if !l.setState(connStateExpectReadyForQuery) { + // protocol out of sync + return fmt.Errorf("unexpected CommandComplete") + } + // ExecSimpleQuery doesn't need to know about this message + + case 'Z': + if !l.setState(connStateIdle) { + // protocol out of sync + return fmt.Errorf("unexpected ReadyForQuery") + } + l.replyChan <- message{t, nil} + + case 'N', 'S': + // ignore + default: + return fmt.Errorf("unexpected message %q from server in listenerConnLoop", t) + } + } +} + +// This is the main routine for the goroutine receiving on the database +// connection. Most of the main logic is in listenerConnLoop. +func (l *ListenerConn) listenerConnMain() { + err := l.listenerConnLoop() + + // listenerConnLoop terminated; we're done, but we still have to clean up. + // Make sure nobody tries to start any new queries by making sure the err + // pointer is set. It is important that we do not overwrite its value; a + // connection could be closed by either this goroutine or one sending on + // the connection -- whoever closes the connection is assumed to have the + // more meaningful error message (as the other one will probably get + // net.errClosed), so that goroutine sets the error we expose while the + // other error is discarded. If the connection is lost while two + // goroutines are operating on the socket, it probably doesn't matter which + // error we expose so we don't try to do anything more complex. + l.connectionLock.Lock() + if l.err == nil { + l.err = err + } + l.cn.Close() + l.connectionLock.Unlock() + + // There might be a query in-flight; make sure nobody's waiting for a + // response to it, since there's not going to be one. + close(l.replyChan) + + // let the listener know we're done + close(l.notificationChan) + + // this ListenerConn is done +} + +// Send a LISTEN query to the server. See ExecSimpleQuery. +func (l *ListenerConn) Listen(channel string) (bool, error) { + return l.ExecSimpleQuery("LISTEN " + QuoteIdentifier(channel)) +} + +// Send an UNLISTEN query to the server. See ExecSimpleQuery. +func (l *ListenerConn) Unlisten(channel string) (bool, error) { + return l.ExecSimpleQuery("UNLISTEN " + QuoteIdentifier(channel)) +} + +// Send `UNLISTEN *` to the server. See ExecSimpleQuery. +func (l *ListenerConn) UnlistenAll() (bool, error) { + return l.ExecSimpleQuery("UNLISTEN *") +} + +// Ping the remote server to make sure it's alive. Non-nil error means the +// connection has failed and should be abandoned. +func (l *ListenerConn) Ping() error { + sent, err := l.ExecSimpleQuery("") + if !sent { + return err + } + if err != nil { + // shouldn't happen + panic(err) + } + return nil +} + +// Attempt to send a query on the connection. Returns an error if sending the +// query failed, and the caller should initiate closure of this connection. +// The caller must be holding senderLock (see acquireSenderLock and +// releaseSenderLock). +func (l *ListenerConn) sendSimpleQuery(q string) (err error) { + defer errRecoverNoErrBadConn(&err) + + // must set connection state before sending the query + if !l.setState(connStateExpectResponse) { + panic("two queries running at the same time") + } + + // Can't use l.cn.writeBuf here because it uses the scratch buffer which + // might get overwritten by listenerConnLoop. + data := writeBuf([]byte("Q\x00\x00\x00\x00")) + b := &data + b.string(q) + l.cn.send(b) + + return nil +} + +// Execute a "simple query" (i.e. one with no bindable parameters) on the +// connection. The possible return values are: +// 1) "executed" is true; the query was executed to completion on the +// database server. If the query failed, err will be set to the error +// returned by the database, otherwise err will be nil. +// 2) If "executed" is false, the query could not be executed on the remote +// server. err will be non-nil. +// +// After a call to ExecSimpleQuery has returned an executed=false value, the +// connection has either been closed or will be closed shortly thereafter, and +// all subsequently executed queries will return an error. +func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) { + if err = l.acquireSenderLock(); err != nil { + return false, err + } + defer l.releaseSenderLock() + + err = l.sendSimpleQuery(q) + if err != nil { + // We can't know what state the protocol is in, so we need to abandon + // this connection. + l.connectionLock.Lock() + // Set the error pointer if it hasn't been set already; see + // listenerConnMain. + if l.err == nil { + l.err = err + } + l.connectionLock.Unlock() + l.cn.c.Close() + return false, err + } + + // now we just wait for a reply.. + for { + m, ok := <-l.replyChan + if !ok { + // We lost the connection to server, don't bother waiting for a + // a response. err should have been set already. + l.connectionLock.Lock() + err := l.err + l.connectionLock.Unlock() + return false, err + } + switch m.typ { + case 'Z': + // sanity check + if m.err != nil { + panic("m.err != nil") + } + // done; err might or might not be set + return true, err + + case 'E': + // sanity check + if m.err == nil { + panic("m.err == nil") + } + // server responded with an error; ReadyForQuery to follow + err = m.err + + default: + return false, fmt.Errorf("unknown response for simple query: %q", m.typ) + } + } +} + +func (l *ListenerConn) Close() error { + l.connectionLock.Lock() + if l.err != nil { + l.connectionLock.Unlock() + return errListenerConnClosed + } + l.err = errListenerConnClosed + l.connectionLock.Unlock() + // We can't send anything on the connection without holding senderLock. + // Simply close the net.Conn to wake up everyone operating on it. + return l.cn.c.Close() +} + +// Err() returns the reason the connection was closed. It is not safe to call +// this function until l.Notify has been closed. +func (l *ListenerConn) Err() error { + return l.err +} + +var errListenerClosed = errors.New("pq: Listener has been closed") + +var ErrChannelAlreadyOpen = errors.New("pq: channel is already open") +var ErrChannelNotOpen = errors.New("pq: channel is not open") + +type ListenerEventType int + +const ( + // Emitted only when the database connection has been initially + // initialized. err will always be nil. + ListenerEventConnected ListenerEventType = iota + + // Emitted after a database connection has been lost, either because of an + // error or because Close has been called. err will be set to the reason + // the database connection was lost. + ListenerEventDisconnected + + // Emitted after a database connection has been re-established after + // connection loss. err will always be nil. After this event has been + // emitted, a nil pq.Notification is sent on the Listener.Notify channel. + ListenerEventReconnected + + // Emitted after a connection to the database was attempted, but failed. + // err will be set to an error describing why the connection attempt did + // not succeed. + ListenerEventConnectionAttemptFailed +) + +type EventCallbackType func(event ListenerEventType, err error) + +// Listener provides an interface for listening to notifications from a +// PostgreSQL database. For general usage information, see section +// "Notifications". +// +// Listener can safely be used from concurrently running goroutines. +type Listener struct { + // Channel for receiving notifications from the database. In some cases a + // nil value will be sent. See section "Notifications" above. + Notify chan *Notification + + name string + minReconnectInterval time.Duration + maxReconnectInterval time.Duration + eventCallback EventCallbackType + + lock sync.Mutex + isClosed bool + reconnectCond *sync.Cond + cn *ListenerConn + connNotificationChan <-chan *Notification + channels map[string]struct{} +} + +// NewListener creates a new database connection dedicated to LISTEN / NOTIFY. +// +// name should be set to a connection string to be used to establish the +// database connection (see section "Connection String Parameters" above). +// +// minReconnectInterval controls the duration to wait before trying to +// re-establish the database connection after connection loss. After each +// consecutive failure this interval is doubled, until maxReconnectInterval is +// reached. Successfully completing the connection establishment procedure +// resets the interval back to minReconnectInterval. +// +// The last parameter eventCallback can be set to a function which will be +// called by the Listener when the state of the underlying database connection +// changes. This callback will be called by the goroutine which dispatches the +// notifications over the Notify channel, so you should try to avoid doing +// potentially time-consuming operations from the callback. +func NewListener(name string, + minReconnectInterval time.Duration, + maxReconnectInterval time.Duration, + eventCallback EventCallbackType) *Listener { + l := &Listener{ + name: name, + minReconnectInterval: minReconnectInterval, + maxReconnectInterval: maxReconnectInterval, + eventCallback: eventCallback, + + channels: make(map[string]struct{}), + + Notify: make(chan *Notification, 32), + } + l.reconnectCond = sync.NewCond(&l.lock) + + go l.listenerMain() + + return l +} + +// Returns the notification channel for this listener. This is the same +// channel as Notify, and will not be recreated during the life time of the +// Listener. +func (l *Listener) NotificationChannel() <-chan *Notification { + return l.Notify +} + +// Listen starts listening for notifications on a channel. Calls to this +// function will block until an acknowledgement has been received from the +// server. Note that Listener automatically re-establishes the connection +// after connection loss, so this function may block indefinitely if the +// connection can not be re-established. +// +// Listen will only fail in three conditions: +// 1) The channel is already open. The returned error will be +// ErrChannelAlreadyOpen. +// 2) The query was executed on the remote server, but PostgreSQL returned an +// error message in response to the query. The returned error will be a +// pq.Error containing the information the server supplied. +// 3) Close is called on the Listener before the request could be completed. +// +// The channel name is case-sensitive. +func (l *Listener) Listen(channel string) error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + + // The server allows you to issue a LISTEN on a channel which is already + // open, but it seems useful to be able to detect this case to spot for + // mistakes in application logic. If the application genuinely does't + // care, it can check the exported error and ignore it. + _, exists := l.channels[channel] + if exists { + return ErrChannelAlreadyOpen + } + + if l.cn != nil { + // If gotResponse is true but error is set, the query was executed on + // the remote server, but resulted in an error. This should be + // relatively rare, so it's fine if we just pass the error to our + // caller. However, if gotResponse is false, we could not complete the + // query on the remote server and our underlying connection is about + // to go away, so we only add relname to l.channels, and wait for + // resync() to take care of the rest. + gotResponse, err := l.cn.Listen(channel) + if gotResponse && err != nil { + return err + } + } + + l.channels[channel] = struct{}{} + for l.cn == nil { + l.reconnectCond.Wait() + // we let go of the mutex for a while + if l.isClosed { + return errListenerClosed + } + } + + return nil +} + +// Unlisten removes a channel from the Listener's channel list. Returns +// ErrChannelNotOpen if the Listener is not listening on the specified channel. +// Returns immediately with no error if there is no connection. Note that you +// might still get notifications for this channel even after Unlisten has +// returned. +// +// The channel name is case-sensitive. +func (l *Listener) Unlisten(channel string) error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + + // Similarly to LISTEN, this is not an error in Postgres, but it seems + // useful to distinguish from the normal conditions. + _, exists := l.channels[channel] + if !exists { + return ErrChannelNotOpen + } + + if l.cn != nil { + // Similarly to Listen (see comment in that function), the caller + // should only be bothered with an error if it came from the backend as + // a response to our query. + gotResponse, err := l.cn.Unlisten(channel) + if gotResponse && err != nil { + return err + } + } + + // Don't bother waiting for resync if there's no connection. + delete(l.channels, channel) + return nil +} + +// UnlistenAll removes all channels from the Listener's channel list. Returns +// immediately with no error if there is no connection. Note that you might +// still get notifications for any of the deleted channels even after +// UnlistenAll has returned. +func (l *Listener) UnlistenAll() error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + + if l.cn != nil { + // Similarly to Listen (see comment in that function), the caller + // should only be bothered with an error if it came from the backend as + // a response to our query. + gotResponse, err := l.cn.UnlistenAll() + if gotResponse && err != nil { + return err + } + } + + // Don't bother waiting for resync if there's no connection. + l.channels = make(map[string]struct{}) + return nil +} + +// Ping the remote server to make sure it's alive. Non-nil return value means +// that there is no active connection. +func (l *Listener) Ping() error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + if l.cn == nil { + return errors.New("no connection") + } + + return l.cn.Ping() +} + +// Clean up after losing the server connection. Returns l.cn.Err(), which +// should have the reason the connection was lost. +func (l *Listener) disconnectCleanup() error { + l.lock.Lock() + defer l.lock.Unlock() + + // sanity check; can't look at Err() until the channel has been closed + select { + case _, ok := <-l.connNotificationChan: + if ok { + panic("connNotificationChan not closed") + } + default: + panic("connNotificationChan not closed") + } + + err := l.cn.Err() + l.cn.Close() + l.cn = nil + return err +} + +// Synchronize the list of channels we want to be listening on with the server +// after the connection has been established. +func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notification) error { + doneChan := make(chan error) + go func() { + for channel := range l.channels { + // If we got a response, return that error to our caller as it's + // going to be more descriptive than cn.Err(). + gotResponse, err := cn.Listen(channel) + if gotResponse && err != nil { + doneChan <- err + return + } + + // If we couldn't reach the server, wait for notificationChan to + // close and then return the error message from the connection, as + // per ListenerConn's interface. + if err != nil { + for _ = range notificationChan { + } + doneChan <- cn.Err() + return + } + } + doneChan <- nil + }() + + // Ignore notifications while synchronization is going on to avoid + // deadlocks. We have to send a nil notification over Notify anyway as + // we can't possibly know which notifications (if any) were lost while + // the connection was down, so there's no reason to try and process + // these messages at all. + for { + select { + case _, ok := <-notificationChan: + if !ok { + notificationChan = nil + } + + case err := <-doneChan: + return err + } + } +} + +// caller should NOT be holding l.lock +func (l *Listener) closed() bool { + l.lock.Lock() + defer l.lock.Unlock() + + return l.isClosed +} + +func (l *Listener) connect() error { + notificationChan := make(chan *Notification, 32) + cn, err := NewListenerConn(l.name, notificationChan) + if err != nil { + return err + } + + l.lock.Lock() + defer l.lock.Unlock() + + err = l.resync(cn, notificationChan) + if err != nil { + cn.Close() + return err + } + + l.cn = cn + l.connNotificationChan = notificationChan + l.reconnectCond.Broadcast() + + return nil +} + +// Close disconnects the Listener from the database and shuts it down. +// Subsequent calls to its methods will return an error. Close returns an +// error if the connection has already been closed. +func (l *Listener) Close() error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + + if l.cn != nil { + l.cn.Close() + } + l.isClosed = true + + return nil +} + +func (l *Listener) emitEvent(event ListenerEventType, err error) { + if l.eventCallback != nil { + l.eventCallback(event, err) + } +} + +// Main logic here: maintain a connection to the server when possible, wait +// for notifications and emit events. +func (l *Listener) listenerConnLoop() { + var nextReconnect time.Time + + reconnectInterval := l.minReconnectInterval + for { + for { + err := l.connect() + if err == nil { + break + } + + if l.closed() { + return + } + l.emitEvent(ListenerEventConnectionAttemptFailed, err) + + time.Sleep(reconnectInterval) + reconnectInterval *= 2 + if reconnectInterval > l.maxReconnectInterval { + reconnectInterval = l.maxReconnectInterval + } + } + + if nextReconnect.IsZero() { + l.emitEvent(ListenerEventConnected, nil) + } else { + l.emitEvent(ListenerEventReconnected, nil) + l.Notify <- nil + } + + reconnectInterval = l.minReconnectInterval + nextReconnect = time.Now().Add(reconnectInterval) + + for { + notification, ok := <-l.connNotificationChan + if !ok { + // lost connection, loop again + break + } + l.Notify <- notification + } + + err := l.disconnectCleanup() + if l.closed() { + return + } + l.emitEvent(ListenerEventDisconnected, err) + + time.Sleep(nextReconnect.Sub(time.Now())) + } +} + +func (l *Listener) listenerMain() { + l.listenerConnLoop() + close(l.Notify) +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/notify_test.go b/Godeps/_workspace/src/github.com/lib/pq/notify_test.go new file mode 100644 index 000000000..fe8941a4e --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/notify_test.go @@ -0,0 +1,574 @@ +package pq + +import ( + "errors" + "fmt" + "io" + "os" + "runtime" + "sync" + "sync/atomic" + "testing" + "time" +) + +var errNilNotification = errors.New("nil notification") + +func expectNotification(t *testing.T, ch <-chan *Notification, relname string, extra string) error { + select { + case n := <-ch: + if n == nil { + return errNilNotification + } + if n.Channel != relname || n.Extra != extra { + return fmt.Errorf("unexpected notification %v", n) + } + return nil + case <-time.After(1500 * time.Millisecond): + return fmt.Errorf("timeout") + } +} + +func expectNoNotification(t *testing.T, ch <-chan *Notification) error { + select { + case n := <-ch: + return fmt.Errorf("unexpected notification %v", n) + case <-time.After(100 * time.Millisecond): + return nil + } +} + +func expectEvent(t *testing.T, eventch <-chan ListenerEventType, et ListenerEventType) error { + select { + case e := <-eventch: + if e != et { + return fmt.Errorf("unexpected event %v", e) + } + return nil + case <-time.After(1500 * time.Millisecond): + panic("expectEvent timeout") + } +} + +func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error { + select { + case e := <-eventch: + return fmt.Errorf("unexpected event %v", e) + case <-time.After(100 * time.Millisecond): + return nil + } +} + +func newTestListenerConn(t *testing.T) (*ListenerConn, <-chan *Notification) { + datname := os.Getenv("PGDATABASE") + sslmode := os.Getenv("PGSSLMODE") + + if datname == "" { + os.Setenv("PGDATABASE", "pqgotest") + } + + if sslmode == "" { + os.Setenv("PGSSLMODE", "disable") + } + + notificationChan := make(chan *Notification) + l, err := NewListenerConn("", notificationChan) + if err != nil { + t.Fatal(err) + } + + return l, notificationChan +} + +func TestNewListenerConn(t *testing.T) { + l, _ := newTestListenerConn(t) + + defer l.Close() +} + +func TestConnListen(t *testing.T) { + l, channel := newTestListenerConn(t) + + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + ok, err := l.Listen("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, channel, "notify_test", "") + if err != nil { + t.Fatal(err) + } +} + +func TestConnUnlisten(t *testing.T) { + l, channel := newTestListenerConn(t) + + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + ok, err := l.Listen("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + + err = expectNotification(t, channel, "notify_test", "") + if err != nil { + t.Fatal(err) + } + + ok, err = l.Unlisten("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + if err != nil { + t.Fatal(err) + } + + err = expectNoNotification(t, channel) + if err != nil { + t.Fatal(err) + } +} + +func TestConnUnlistenAll(t *testing.T) { + l, channel := newTestListenerConn(t) + + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + ok, err := l.Listen("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + + err = expectNotification(t, channel, "notify_test", "") + if err != nil { + t.Fatal(err) + } + + ok, err = l.UnlistenAll() + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + if err != nil { + t.Fatal(err) + } + + err = expectNoNotification(t, channel) + if err != nil { + t.Fatal(err) + } +} + +func TestConnClose(t *testing.T) { + l, _ := newTestListenerConn(t) + defer l.Close() + + err := l.Close() + if err != nil { + t.Fatal(err) + } + err = l.Close() + if err != errListenerConnClosed { + t.Fatalf("expected errListenerConnClosed; got %v", err) + } +} + +func TestConnPing(t *testing.T) { + l, _ := newTestListenerConn(t) + defer l.Close() + err := l.Ping() + if err != nil { + t.Fatal(err) + } + err = l.Close() + if err != nil { + t.Fatal(err) + } + err = l.Ping() + if err != errListenerConnClosed { + t.Fatalf("expected errListenerConnClosed; got %v", err) + } +} + +// Test for deadlock where a query fails while another one is queued +func TestConnExecDeadlock(t *testing.T) { + l, _ := newTestListenerConn(t) + defer l.Close() + + var wg sync.WaitGroup + wg.Add(2) + + go func() { + l.ExecSimpleQuery("SELECT pg_sleep(60)") + wg.Done() + }() + runtime.Gosched() + go func() { + l.ExecSimpleQuery("SELECT 1") + wg.Done() + }() + // give the two goroutines some time to get into position + runtime.Gosched() + // calls Close on the net.Conn; equivalent to a network failure + l.Close() + + var done int32 = 0 + go func() { + time.Sleep(10 * time.Second) + if atomic.LoadInt32(&done) != 1 { + panic("timed out") + } + }() + wg.Wait() + atomic.StoreInt32(&done, 1) +} + +// Test for ListenerConn being closed while a slow query is executing +func TestListenerConnCloseWhileQueryIsExecuting(t *testing.T) { + l, _ := newTestListenerConn(t) + defer l.Close() + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + sent, err := l.ExecSimpleQuery("SELECT pg_sleep(60)") + if sent { + panic("expected sent=false") + } + // could be any of a number of errors + if err == nil { + panic("expected error") + } + wg.Done() + }() + // give the above goroutine some time to get into position + runtime.Gosched() + err := l.Close() + if err != nil { + t.Fatal(err) + } + var done int32 = 0 + go func() { + time.Sleep(10 * time.Second) + if atomic.LoadInt32(&done) != 1 { + panic("timed out") + } + }() + wg.Wait() + atomic.StoreInt32(&done, 1) +} + +func TestNotifyExtra(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + if getServerVersion(t, db) < 90000 { + t.Skip("skipping NOTIFY payload test since the server does not appear to support it") + } + + l, channel := newTestListenerConn(t) + defer l.Close() + + ok, err := l.Listen("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test, 'something'") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, channel, "notify_test", "something") + if err != nil { + t.Fatal(err) + } +} + +// create a new test listener and also set the timeouts +func newTestListenerTimeout(t *testing.T, min time.Duration, max time.Duration) (*Listener, <-chan ListenerEventType) { + datname := os.Getenv("PGDATABASE") + sslmode := os.Getenv("PGSSLMODE") + + if datname == "" { + os.Setenv("PGDATABASE", "pqgotest") + } + + if sslmode == "" { + os.Setenv("PGSSLMODE", "disable") + } + + eventch := make(chan ListenerEventType, 16) + l := NewListener("", min, max, func(t ListenerEventType, err error) { eventch <- t }) + err := expectEvent(t, eventch, ListenerEventConnected) + if err != nil { + t.Fatal(err) + } + return l, eventch +} + +func newTestListener(t *testing.T) (*Listener, <-chan ListenerEventType) { + return newTestListenerTimeout(t, time.Hour, time.Hour) +} + +func TestListenerListen(t *testing.T) { + l, _ := newTestListener(t) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } +} + +func TestListenerUnlisten(t *testing.T) { + l, _ := newTestListener(t) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = l.Unlisten("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNoNotification(t, l.Notify) + if err != nil { + t.Fatal(err) + } +} + +func TestListenerUnlistenAll(t *testing.T) { + l, _ := newTestListener(t) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = l.UnlistenAll() + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNoNotification(t, l.Notify) + if err != nil { + t.Fatal(err) + } +} + +func TestListenerFailedQuery(t *testing.T) { + l, eventch := newTestListener(t) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } + + // shouldn't cause a disconnect + ok, err := l.cn.ExecSimpleQuery("SELECT error") + if !ok { + t.Fatalf("could not send query to server: %v", err) + } + _, ok = err.(PGError) + if !ok { + t.Fatalf("unexpected error %v", err) + } + err = expectNoEvent(t, eventch) + if err != nil { + t.Fatal(err) + } + + // should still work + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } +} + +func TestListenerReconnect(t *testing.T) { + l, eventch := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } + + // kill the connection and make sure it comes back up + ok, err := l.cn.ExecSimpleQuery("SELECT pg_terminate_backend(pg_backend_pid())") + if ok { + t.Fatalf("could not kill the connection: %v", err) + } + if err != io.EOF { + t.Fatalf("unexpected error %v", err) + } + err = expectEvent(t, eventch, ListenerEventDisconnected) + if err != nil { + t.Fatal(err) + } + err = expectEvent(t, eventch, ListenerEventReconnected) + if err != nil { + t.Fatal(err) + } + + // should still work + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + // should get nil after Reconnected + err = expectNotification(t, l.Notify, "", "") + if err != errNilNotification { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } +} + +func TestListenerClose(t *testing.T) { + l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour) + defer l.Close() + + err := l.Close() + if err != nil { + t.Fatal(err) + } + err = l.Close() + if err != errListenerClosed { + t.Fatalf("expected errListenerClosed; got %v", err) + } +} + +func TestListenerPing(t *testing.T) { + l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour) + defer l.Close() + + err := l.Ping() + if err != nil { + t.Fatal(err) + } + + err = l.Close() + if err != nil { + t.Fatal(err) + } + + err = l.Ping() + if err != errListenerClosed { + t.Fatalf("expected errListenerClosed; got %v", err) + } +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/oid/doc.go b/Godeps/_workspace/src/github.com/lib/pq/oid/doc.go new file mode 100644 index 000000000..caaede248 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/oid/doc.go @@ -0,0 +1,6 @@ +// Package oid contains OID constants +// as defined by the Postgres server. +package oid + +// Oid is a Postgres Object ID. +type Oid uint32 diff --git a/Godeps/_workspace/src/github.com/lib/pq/oid/gen.go b/Godeps/_workspace/src/github.com/lib/pq/oid/gen.go new file mode 100644 index 000000000..cd4aea808 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/oid/gen.go @@ -0,0 +1,74 @@ +// +build ignore + +// Generate the table of OID values +// Run with 'go run gen.go'. +package main + +import ( + "database/sql" + "fmt" + "log" + "os" + "os/exec" + + _ "github.com/lib/pq" +) + +func main() { + datname := os.Getenv("PGDATABASE") + sslmode := os.Getenv("PGSSLMODE") + + if datname == "" { + os.Setenv("PGDATABASE", "pqgotest") + } + + if sslmode == "" { + os.Setenv("PGSSLMODE", "disable") + } + + db, err := sql.Open("postgres", "") + if err != nil { + log.Fatal(err) + } + cmd := exec.Command("gofmt") + cmd.Stderr = os.Stderr + w, err := cmd.StdinPipe() + if err != nil { + log.Fatal(err) + } + f, err := os.Create("types.go") + if err != nil { + log.Fatal(err) + } + cmd.Stdout = f + err = cmd.Start() + if err != nil { + log.Fatal(err) + } + fmt.Fprintln(w, "// generated by 'go run gen.go'; do not edit") + fmt.Fprintln(w, "\npackage oid") + fmt.Fprintln(w, "const (") + rows, err := db.Query(` + SELECT typname, oid + FROM pg_type WHERE oid < 10000 + ORDER BY oid; + `) + if err != nil { + log.Fatal(err) + } + var name string + var oid int + for rows.Next() { + err = rows.Scan(&name, &oid) + if err != nil { + log.Fatal(err) + } + fmt.Fprintf(w, "T_%s Oid = %d\n", name, oid) + } + if err = rows.Err(); err != nil { + log.Fatal(err) + } + fmt.Fprintln(w, ")") + w.Close() + cmd.Wait() +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/oid/types.go b/Godeps/_workspace/src/github.com/lib/pq/oid/types.go new file mode 100644 index 000000000..03df05a61 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/oid/types.go @@ -0,0 +1,161 @@ +// generated by 'go run gen.go'; do not edit + +package oid + +const ( + T_bool Oid = 16 + T_bytea Oid = 17 + T_char Oid = 18 + T_name Oid = 19 + T_int8 Oid = 20 + T_int2 Oid = 21 + T_int2vector Oid = 22 + T_int4 Oid = 23 + T_regproc Oid = 24 + T_text Oid = 25 + T_oid Oid = 26 + T_tid Oid = 27 + T_xid Oid = 28 + T_cid Oid = 29 + T_oidvector Oid = 30 + T_pg_type Oid = 71 + T_pg_attribute Oid = 75 + T_pg_proc Oid = 81 + T_pg_class Oid = 83 + T_json Oid = 114 + T_xml Oid = 142 + T__xml Oid = 143 + T_pg_node_tree Oid = 194 + T__json Oid = 199 + T_smgr Oid = 210 + T_point Oid = 600 + T_lseg Oid = 601 + T_path Oid = 602 + T_box Oid = 603 + T_polygon Oid = 604 + T_line Oid = 628 + T__line Oid = 629 + T_cidr Oid = 650 + T__cidr Oid = 651 + T_float4 Oid = 700 + T_float8 Oid = 701 + T_abstime Oid = 702 + T_reltime Oid = 703 + T_tinterval Oid = 704 + T_unknown Oid = 705 + T_circle Oid = 718 + T__circle Oid = 719 + T_money Oid = 790 + T__money Oid = 791 + T_macaddr Oid = 829 + T_inet Oid = 869 + T__bool Oid = 1000 + T__bytea Oid = 1001 + T__char Oid = 1002 + T__name Oid = 1003 + T__int2 Oid = 1005 + T__int2vector Oid = 1006 + T__int4 Oid = 1007 + T__regproc Oid = 1008 + T__text Oid = 1009 + T__tid Oid = 1010 + T__xid Oid = 1011 + T__cid Oid = 1012 + T__oidvector Oid = 1013 + T__bpchar Oid = 1014 + T__varchar Oid = 1015 + T__int8 Oid = 1016 + T__point Oid = 1017 + T__lseg Oid = 1018 + T__path Oid = 1019 + T__box Oid = 1020 + T__float4 Oid = 1021 + T__float8 Oid = 1022 + T__abstime Oid = 1023 + T__reltime Oid = 1024 + T__tinterval Oid = 1025 + T__polygon Oid = 1027 + T__oid Oid = 1028 + T_aclitem Oid = 1033 + T__aclitem Oid = 1034 + T__macaddr Oid = 1040 + T__inet Oid = 1041 + T_bpchar Oid = 1042 + T_varchar Oid = 1043 + T_date Oid = 1082 + T_time Oid = 1083 + T_timestamp Oid = 1114 + T__timestamp Oid = 1115 + T__date Oid = 1182 + T__time Oid = 1183 + T_timestamptz Oid = 1184 + T__timestamptz Oid = 1185 + T_interval Oid = 1186 + T__interval Oid = 1187 + T__numeric Oid = 1231 + T_pg_database Oid = 1248 + T__cstring Oid = 1263 + T_timetz Oid = 1266 + T__timetz Oid = 1270 + T_bit Oid = 1560 + T__bit Oid = 1561 + T_varbit Oid = 1562 + T__varbit Oid = 1563 + T_numeric Oid = 1700 + T_refcursor Oid = 1790 + T__refcursor Oid = 2201 + T_regprocedure Oid = 2202 + T_regoper Oid = 2203 + T_regoperator Oid = 2204 + T_regclass Oid = 2205 + T_regtype Oid = 2206 + T__regprocedure Oid = 2207 + T__regoper Oid = 2208 + T__regoperator Oid = 2209 + T__regclass Oid = 2210 + T__regtype Oid = 2211 + T_record Oid = 2249 + T_cstring Oid = 2275 + T_any Oid = 2276 + T_anyarray Oid = 2277 + T_void Oid = 2278 + T_trigger Oid = 2279 + T_language_handler Oid = 2280 + T_internal Oid = 2281 + T_opaque Oid = 2282 + T_anyelement Oid = 2283 + T__record Oid = 2287 + T_anynonarray Oid = 2776 + T_pg_authid Oid = 2842 + T_pg_auth_members Oid = 2843 + T__txid_snapshot Oid = 2949 + T_uuid Oid = 2950 + T__uuid Oid = 2951 + T_txid_snapshot Oid = 2970 + T_fdw_handler Oid = 3115 + T_anyenum Oid = 3500 + T_tsvector Oid = 3614 + T_tsquery Oid = 3615 + T_gtsvector Oid = 3642 + T__tsvector Oid = 3643 + T__gtsvector Oid = 3644 + T__tsquery Oid = 3645 + T_regconfig Oid = 3734 + T__regconfig Oid = 3735 + T_regdictionary Oid = 3769 + T__regdictionary Oid = 3770 + T_anyrange Oid = 3831 + T_event_trigger Oid = 3838 + T_int4range Oid = 3904 + T__int4range Oid = 3905 + T_numrange Oid = 3906 + T__numrange Oid = 3907 + T_tsrange Oid = 3908 + T__tsrange Oid = 3909 + T_tstzrange Oid = 3910 + T__tstzrange Oid = 3911 + T_daterange Oid = 3912 + T__daterange Oid = 3913 + T_int8range Oid = 3926 + T__int8range Oid = 3927 +) diff --git a/Godeps/_workspace/src/github.com/lib/pq/ssl_test.go b/Godeps/_workspace/src/github.com/lib/pq/ssl_test.go new file mode 100644 index 000000000..932b336f5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/ssl_test.go @@ -0,0 +1,226 @@ +package pq + +// This file contains SSL tests + +import ( + _ "crypto/sha256" + "crypto/x509" + "database/sql" + "fmt" + "os" + "path/filepath" + "testing" +) + +func maybeSkipSSLTests(t *testing.T) { + // Require some special variables for testing certificates + if os.Getenv("PQSSLCERTTEST_PATH") == "" { + t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests") + } + + value := os.Getenv("PQGOSSLTESTS") + if value == "" || value == "0" { + t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests") + } else if value != "1" { + t.Fatalf("unexpected value %q for PQGOSSLTESTS", value) + } +} + +func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) { + db, err := openTestConnConninfo(conninfo) + if err != nil { + // should never fail + t.Fatal(err) + } + // Do something with the connection to see whether it's working or not. + tx, err := db.Begin() + if err == nil { + return db, tx.Rollback() + } + _ = db.Close() + return nil, err +} + +func checkSSLSetup(t *testing.T, conninfo string) { + db, err := openSSLConn(t, conninfo) + if err == nil { + db.Close() + t.Fatalf("expected error with conninfo=%q", conninfo) + } +} + +// Connect over SSL and run a simple query to test the basics +func TestSSLConnection(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + db, err := openSSLConn(t, "sslmode=require user=pqgossltest") + if err != nil { + t.Fatal(err) + } + rows, err := db.Query("SELECT 1") + if err != nil { + t.Fatal(err) + } + rows.Close() +} + +// Test sslmode=verify-full +func TestSSLVerifyFull(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + // Not OK according to the system CA + _, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest") + if err == nil { + t.Fatal("expected error") + } + _, ok := err.(x509.UnknownAuthorityError) + if !ok { + t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err) + } + + rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt") + rootCert := "sslrootcert=" + rootCertPath + " " + // No match on Common Name + _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest") + if err == nil { + t.Fatal("expected error") + } + _, ok = err.(x509.HostnameError) + if !ok { + t.Fatalf("expected x509.HostnameError, got %#+v", err) + } + // OK + _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest") + if err != nil { + t.Fatal(err) + } +} + +// Test sslmode=verify-ca +func TestSSLVerifyCA(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + // Not OK according to the system CA + _, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest") + if err == nil { + t.Fatal("expected error") + } + _, ok := err.(x509.UnknownAuthorityError) + if !ok { + t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err) + } + + rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt") + rootCert := "sslrootcert=" + rootCertPath + " " + // No match on Common Name, but that's OK + _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest") + if err != nil { + t.Fatal(err) + } + // Everything OK + _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest") + if err != nil { + t.Fatal(err) + } +} + +func getCertConninfo(t *testing.T, source string) string { + var sslkey string + var sslcert string + + certpath := os.Getenv("PQSSLCERTTEST_PATH") + + switch source { + case "missingkey": + sslkey = "/tmp/filedoesnotexist" + sslcert = filepath.Join(certpath, "postgresql.crt") + case "missingcert": + sslkey = filepath.Join(certpath, "postgresql.key") + sslcert = "/tmp/filedoesnotexist" + case "certtwice": + sslkey = filepath.Join(certpath, "postgresql.crt") + sslcert = filepath.Join(certpath, "postgresql.crt") + case "valid": + sslkey = filepath.Join(certpath, "postgresql.key") + sslcert = filepath.Join(certpath, "postgresql.crt") + default: + t.Fatalf("invalid source %q", source) + } + return fmt.Sprintf("sslmode=require user=pqgosslcert sslkey=%s sslcert=%s", sslkey, sslcert) +} + +// Authenticate over SSL using client certificates +func TestSSLClientCertificates(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + // Should also fail without a valid certificate + db, err := openSSLConn(t, "sslmode=require user=pqgosslcert") + if err == nil { + db.Close() + t.Fatal("expected error") + } + pge, ok := err.(*Error) + if !ok { + t.Fatal("expected pq.Error") + } + if pge.Code.Name() != "invalid_authorization_specification" { + t.Fatalf("unexpected error code %q", pge.Code.Name()) + } + + // Should work + db, err = openSSLConn(t, getCertConninfo(t, "valid")) + if err != nil { + t.Fatal(err) + } + rows, err := db.Query("SELECT 1") + if err != nil { + t.Fatal(err) + } + rows.Close() +} + +// Test errors with ssl certificates +func TestSSLClientCertificatesMissingFiles(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + // Key missing, should fail + _, err := openSSLConn(t, getCertConninfo(t, "missingkey")) + if err == nil { + t.Fatal("expected error") + } + // should be a PathError + _, ok := err.(*os.PathError) + if !ok { + t.Fatalf("expected PathError, got %#+v", err) + } + + // Cert missing, should fail + _, err = openSSLConn(t, getCertConninfo(t, "missingcert")) + if err == nil { + t.Fatal("expected error") + } + // should be a PathError + _, ok = err.(*os.PathError) + if !ok { + t.Fatalf("expected PathError, got %#+v", err) + } + + // Key has wrong permissions, should fail + _, err = openSSLConn(t, getCertConninfo(t, "certtwice")) + if err == nil { + t.Fatal("expected error") + } + if err != ErrSSLKeyHasWorldPermissions { + t.Fatalf("expected ErrSSLKeyHasWorldPermissions, got %#+v", err) + } +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/url.go b/Godeps/_workspace/src/github.com/lib/pq/url.go new file mode 100644 index 000000000..9bac95c48 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/url.go @@ -0,0 +1,76 @@ +package pq + +import ( + "fmt" + nurl "net/url" + "sort" + "strings" +) + +// ParseURL no longer needs to be used by clients of this library since supplying a URL as a +// connection string to sql.Open() is now supported: +// +// sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full") +// +// It remains exported here for backwards-compatibility. +// +// ParseURL converts a url to a connection string for driver.Open. +// Example: +// +// "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full" +// +// converts to: +// +// "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full" +// +// A minimal example: +// +// "postgres://" +// +// This will be blank, causing driver.Open to use all of the defaults +func ParseURL(url string) (string, error) { + u, err := nurl.Parse(url) + if err != nil { + return "", err + } + + if u.Scheme != "postgres" && u.Scheme != "postgresql" { + return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) + } + + var kvs []string + escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`) + accrue := func(k, v string) { + if v != "" { + kvs = append(kvs, k+"="+escaper.Replace(v)) + } + } + + if u.User != nil { + v := u.User.Username() + accrue("user", v) + + v, _ = u.User.Password() + accrue("password", v) + } + + i := strings.Index(u.Host, ":") + if i < 0 { + accrue("host", u.Host) + } else { + accrue("host", u.Host[:i]) + accrue("port", u.Host[i+1:]) + } + + if u.Path != "" { + accrue("dbname", u.Path[1:]) + } + + q := u.Query() + for k := range q { + accrue(k, q.Get(k)) + } + + sort.Strings(kvs) // Makes testing easier (not a performance concern) + return strings.Join(kvs, " "), nil +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/url_test.go b/Godeps/_workspace/src/github.com/lib/pq/url_test.go new file mode 100644 index 000000000..29f4a7c75 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/url_test.go @@ -0,0 +1,54 @@ +package pq + +import ( + "testing" +) + +func TestSimpleParseURL(t *testing.T) { + expected := "host=hostname.remote" + str, err := ParseURL("postgres://hostname.remote") + if err != nil { + t.Fatal(err) + } + + if str != expected { + t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected) + } +} + +func TestFullParseURL(t *testing.T) { + expected := `dbname=database host=hostname.remote password=top\ secret port=1234 user=username` + str, err := ParseURL("postgres://username:top%20secret@hostname.remote:1234/database") + if err != nil { + t.Fatal(err) + } + + if str != expected { + t.Fatalf("unexpected result from ParseURL:\n+ %s\n- %s", str, expected) + } +} + +func TestInvalidProtocolParseURL(t *testing.T) { + _, err := ParseURL("http://hostname.remote") + switch err { + case nil: + t.Fatal("Expected an error from parsing invalid protocol") + default: + msg := "invalid connection protocol: http" + if err.Error() != msg { + t.Fatalf("Unexpected error message:\n+ %s\n- %s", + err.Error(), msg) + } + } +} + +func TestMinimalURL(t *testing.T) { + cs, err := ParseURL("postgres://") + if err != nil { + t.Fatal(err) + } + + if cs != "" { + t.Fatalf("expected blank connection string, got: %q", cs) + } +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/user_posix.go b/Godeps/_workspace/src/github.com/lib/pq/user_posix.go new file mode 100644 index 000000000..e937d7d08 --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/user_posix.go @@ -0,0 +1,24 @@ +// Package pq is a pure Go Postgres driver for the database/sql package. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package pq + +import ( + "os" + "os/user" +) + +func userCurrent() (string, error) { + u, err := user.Current() + if err == nil { + return u.Username, nil + } + + name := os.Getenv("USER") + if name != "" { + return name, nil + } + + return "", ErrCouldNotDetectUsername +} diff --git a/Godeps/_workspace/src/github.com/lib/pq/user_windows.go b/Godeps/_workspace/src/github.com/lib/pq/user_windows.go new file mode 100644 index 000000000..2b691267b --- /dev/null +++ b/Godeps/_workspace/src/github.com/lib/pq/user_windows.go @@ -0,0 +1,27 @@ +// Package pq is a pure Go Postgres driver for the database/sql package. +package pq + +import ( + "path/filepath" + "syscall" +) + +// Perform Windows user name lookup identically to libpq. +// +// The PostgreSQL code makes use of the legacy Win32 function +// GetUserName, and that function has not been imported into stock Go. +// GetUserNameEx is available though, the difference being that a +// wider range of names are available. To get the output to be the +// same as GetUserName, only the base (or last) component of the +// result is returned. +func userCurrent() (string, error) { + pw_name := make([]uint16, 128) + pwname_size := uint32(len(pw_name)) - 1 + err := syscall.GetUserNameEx(syscall.NameSamCompatible, &pw_name[0], &pwname_size) + if err != nil { + return "", ErrCouldNotDetectUsername + } + s := syscall.UTF16ToString(pw_name) + u := filepath.Base(s) + return u, nil +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/AUTHORS b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/AUTHORS new file mode 100644 index 000000000..d84404ee2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/AUTHORS @@ -0,0 +1,4 @@ +# This is the official list of Protocol Authors for copyright purposes. + +Audrius Butkevicius +Jakob Borg diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/CONTRIBUTING.md new file mode 100644 index 000000000..67e6a9c70 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/CONTRIBUTING.md @@ -0,0 +1,76 @@ +## Reporting Bugs + +Please file bugs in the [Github Issue +Tracker](https://github.com/syncthing/protocol/issues). + +## Contributing Code + +Every contribution is welcome. Following the points below will make this +a smoother process. + +Individuals making significant and valuable contributions are given +commit-access to the project. If you make a significant contribution and +are not considered for commit-access, please contact any of the +Syncthing core team members. + +All nontrivial contributions should go through the pull request +mechanism for internal review. Determining what is "nontrivial" is left +at the discretion of the contributor. + +### Authorship + +All code authors are listed in the AUTHORS file. Commits must be made +with the same name and email as listed in the AUTHORS file. To +accomplish this, ensure that your git configuration is set correctly +prior to making your first commit; + + $ git config --global user.name "Jane Doe" + $ git config --global user.email janedoe@example.com + +You must be reachable on the given email address. If you do not wish to +use your real name for whatever reason, using a nickname or pseudonym is +perfectly acceptable. + +## Coding Style + +- Follow the conventions laid out in [Effective Go](https://golang.org/doc/effective_go.html) + as much as makes sense. + +- All text files use Unix line endings. + +- Each commit should be `go fmt` clean. + +- The commit message subject should be a single short sentence + describing the change, starting with a capital letter. + +- Commits that resolve an existing issue must include the issue number + as `(fixes #123)` at the end of the commit message subject. + +- Imports are grouped per `goimports` standard; that is, standard + library first, then third party libraries after a blank line. + +- A contribution solving a single issue or introducing a single new + feature should probably be a single commit based on the current + `master` branch. You may be asked to "rebase" or "squash" your pull + request to make sure this is the case, especially if there have been + amendments during review. + +## Licensing + +All contributions are made under the same MIT license as the rest of the +project, except documentation, user interface text and translation +strings which are licensed under the Creative Commons Attribution 4.0 +International License. You retain the copyright to code you have +written. + +When accepting your first contribution, the maintainer of the project +will ensure that you are added to the AUTHORS file. You are welcome to +add yourself as a separate commit in your first pull request. + +## Tests + +Yes please! + +## License + +MIT diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/LICENSE b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/LICENSE new file mode 100644 index 000000000..6f6960a75 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2014-2015 The Protocol Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +- The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/README.md b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/README.md new file mode 100644 index 000000000..bcba44b42 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/README.md @@ -0,0 +1,13 @@ +The BEPv1 Protocol +================== + +[![Latest Build](http://img.shields.io/jenkins/s/http/build.syncthing.net/protocol.svg?style=flat-square)](http://build.syncthing.net/job/protocol/lastBuild/) +[![API Documentation](http://img.shields.io/badge/api-Godoc-blue.svg?style=flat-square)](http://godoc.org/github.com/syncthing/protocol) +[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](http://opensource.org/licenses/MIT) + +This is the protocol implementation used by Syncthing. + +License +======= + +MIT diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/common_test.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/common_test.go new file mode 100644 index 000000000..8f1028078 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/common_test.go @@ -0,0 +1,81 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import ( + "io" + "time" +) + +type TestModel struct { + data []byte + folder string + name string + offset int64 + size int + hash []byte + flags uint32 + options []Option + closedCh chan bool +} + +func newTestModel() *TestModel { + return &TestModel{ + closedCh: make(chan bool), + } +} + +func (t *TestModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) { +} + +func (t *TestModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) { +} + +func (t *TestModel) Request(deviceID DeviceID, folder, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error { + t.folder = folder + t.name = name + t.offset = offset + t.size = len(buf) + t.hash = hash + t.flags = flags + t.options = options + copy(buf, t.data) + return nil +} + +func (t *TestModel) Close(deviceID DeviceID, err error) { + close(t.closedCh) +} + +func (t *TestModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) { +} + +func (t *TestModel) isClosed() bool { + select { + case <-t.closedCh: + return true + case <-time.After(1 * time.Second): + return false // Timeout + } +} + +type ErrPipe struct { + io.PipeWriter + written int + max int + err error + closed bool +} + +func (e *ErrPipe) Write(data []byte) (int, error) { + if e.closed { + return 0, e.err + } + if e.written+len(data) > e.max { + n, _ := e.PipeWriter.Write(data[:e.max-e.written]) + e.PipeWriter.CloseWithError(e.err) + e.closed = true + return n, e.err + } + return e.PipeWriter.Write(data) +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/compression.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/compression.go new file mode 100644 index 000000000..9e17213b6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/compression.go @@ -0,0 +1,53 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +import "fmt" + +type Compression int + +const ( + CompressMetadata Compression = iota // zero value is the default, default should be "metadata" + CompressNever + CompressAlways + + compressionThreshold = 128 // don't bother compressing messages smaller than this many bytes +) + +var compressionMarshal = map[Compression]string{ + CompressNever: "never", + CompressMetadata: "metadata", + CompressAlways: "always", +} + +var compressionUnmarshal = map[string]Compression{ + // Legacy + "false": CompressNever, + "true": CompressMetadata, + + // Current + "never": CompressNever, + "metadata": CompressMetadata, + "always": CompressAlways, +} + +func (c Compression) String() string { + s, ok := compressionMarshal[c] + if !ok { + return fmt.Sprintf("unknown:%d", c) + } + return s +} + +func (c Compression) GoString() string { + return fmt.Sprintf("%q", c.String()) +} + +func (c Compression) MarshalText() ([]byte, error) { + return []byte(compressionMarshal[c]), nil +} + +func (c *Compression) UnmarshalText(bs []byte) error { + *c = compressionUnmarshal[string(bs)] + return nil +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/compression_test.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/compression_test.go new file mode 100644 index 000000000..90312344c --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/compression_test.go @@ -0,0 +1,49 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +import "testing" + +func TestCompressionMarshal(t *testing.T) { + uTestcases := []struct { + s string + c Compression + }{ + {"true", CompressMetadata}, + {"false", CompressNever}, + {"never", CompressNever}, + {"metadata", CompressMetadata}, + {"always", CompressAlways}, + {"whatever", CompressMetadata}, + } + + mTestcases := []struct { + s string + c Compression + }{ + {"never", CompressNever}, + {"metadata", CompressMetadata}, + {"always", CompressAlways}, + } + + var c Compression + for _, tc := range uTestcases { + err := c.UnmarshalText([]byte(tc.s)) + if err != nil { + t.Error(err) + } + if c != tc.c { + t.Errorf("%s unmarshalled to %d, not %d", tc.s, c, tc.c) + } + } + + for _, tc := range mTestcases { + bs, err := tc.c.MarshalText() + if err != nil { + t.Error(err) + } + if s := string(bs); s != tc.s { + t.Errorf("%d marshalled to %q, not %q", tc.c, s, tc.s) + } + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/conflict_test.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/conflict_test.go new file mode 100644 index 000000000..ef5c44d7e --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/conflict_test.go @@ -0,0 +1,23 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +import "testing" + +func TestWinsConflict(t *testing.T) { + testcases := [][2]FileInfo{ + // The first should always win over the second + {{Modified: 42}, {Modified: 41}}, + {{Modified: 41}, {Modified: 42, Flags: FlagDeleted}}, + {{Modified: 41, Version: Vector{{42, 2}, {43, 1}}}, {Modified: 41, Version: Vector{{42, 1}, {43, 2}}}}, + } + + for _, tc := range testcases { + if !tc[0].WinsConflict(tc[1]) { + t.Errorf("%v should win over %v", tc[0], tc[1]) + } + if tc[1].WinsConflict(tc[0]) { + t.Errorf("%v should not win over %v", tc[1], tc[0]) + } + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/counting.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/counting.go new file mode 100644 index 000000000..d441ed311 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/counting.go @@ -0,0 +1,62 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import ( + "io" + "sync/atomic" + "time" +) + +type countingReader struct { + io.Reader + tot int64 // bytes + last int64 // unix nanos +} + +var ( + totalIncoming int64 + totalOutgoing int64 +) + +func (c *countingReader) Read(bs []byte) (int, error) { + n, err := c.Reader.Read(bs) + atomic.AddInt64(&c.tot, int64(n)) + atomic.AddInt64(&totalIncoming, int64(n)) + atomic.StoreInt64(&c.last, time.Now().UnixNano()) + return n, err +} + +func (c *countingReader) Tot() int64 { + return atomic.LoadInt64(&c.tot) +} + +func (c *countingReader) Last() time.Time { + return time.Unix(0, atomic.LoadInt64(&c.last)) +} + +type countingWriter struct { + io.Writer + tot int64 // bytes + last int64 // unix nanos +} + +func (c *countingWriter) Write(bs []byte) (int, error) { + n, err := c.Writer.Write(bs) + atomic.AddInt64(&c.tot, int64(n)) + atomic.AddInt64(&totalOutgoing, int64(n)) + atomic.StoreInt64(&c.last, time.Now().UnixNano()) + return n, err +} + +func (c *countingWriter) Tot() int64 { + return atomic.LoadInt64(&c.tot) +} + +func (c *countingWriter) Last() time.Time { + return time.Unix(0, atomic.LoadInt64(&c.last)) +} + +func TotalInOut() (int64, int64) { + return atomic.LoadInt64(&totalIncoming), atomic.LoadInt64(&totalOutgoing) +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/debug.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/debug.go new file mode 100644 index 000000000..435d7f5d2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/debug.go @@ -0,0 +1,15 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import ( + "os" + "strings" + + "github.com/calmh/logger" +) + +var ( + debug = strings.Contains(os.Getenv("STTRACE"), "protocol") || os.Getenv("STTRACE") == "all" + l = logger.DefaultLogger +) diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/deviceid.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/deviceid.go new file mode 100644 index 000000000..2e0334a6a --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/deviceid.go @@ -0,0 +1,163 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import ( + "bytes" + "crypto/sha256" + "encoding/base32" + "encoding/binary" + "errors" + "fmt" + "regexp" + "strings" + + "github.com/calmh/luhn" +) + +type DeviceID [32]byte + +var LocalDeviceID = DeviceID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + +// NewDeviceID generates a new device ID from the raw bytes of a certificate +func NewDeviceID(rawCert []byte) DeviceID { + var n DeviceID + hf := sha256.New() + hf.Write(rawCert) + hf.Sum(n[:0]) + return n +} + +func DeviceIDFromString(s string) (DeviceID, error) { + var n DeviceID + err := n.UnmarshalText([]byte(s)) + return n, err +} + +func DeviceIDFromBytes(bs []byte) DeviceID { + var n DeviceID + if len(bs) != len(n) { + panic("incorrect length of byte slice representing device ID") + } + copy(n[:], bs) + return n +} + +// String returns the canonical string representation of the device ID +func (n DeviceID) String() string { + id := base32.StdEncoding.EncodeToString(n[:]) + id = strings.Trim(id, "=") + id, err := luhnify(id) + if err != nil { + // Should never happen + panic(err) + } + id = chunkify(id) + return id +} + +func (n DeviceID) GoString() string { + return n.String() +} + +func (n DeviceID) Compare(other DeviceID) int { + return bytes.Compare(n[:], other[:]) +} + +func (n DeviceID) Equals(other DeviceID) bool { + return bytes.Compare(n[:], other[:]) == 0 +} + +// Short returns an integer representing bits 0-63 of the device ID. +func (n DeviceID) Short() uint64 { + return binary.BigEndian.Uint64(n[:]) +} + +func (n *DeviceID) MarshalText() ([]byte, error) { + return []byte(n.String()), nil +} + +func (n *DeviceID) UnmarshalText(bs []byte) error { + id := string(bs) + id = strings.Trim(id, "=") + id = strings.ToUpper(id) + id = untypeoify(id) + id = unchunkify(id) + + var err error + switch len(id) { + case 56: + // New style, with check digits + id, err = unluhnify(id) + if err != nil { + return err + } + fallthrough + case 52: + // Old style, no check digits + dec, err := base32.StdEncoding.DecodeString(id + "====") + if err != nil { + return err + } + copy(n[:], dec) + return nil + default: + return errors.New("device ID invalid: incorrect length") + } +} + +func luhnify(s string) (string, error) { + if len(s) != 52 { + panic("unsupported string length") + } + + res := make([]string, 0, 4) + for i := 0; i < 4; i++ { + p := s[i*13 : (i+1)*13] + l, err := luhn.Base32.Generate(p) + if err != nil { + return "", err + } + res = append(res, fmt.Sprintf("%s%c", p, l)) + } + return res[0] + res[1] + res[2] + res[3], nil +} + +func unluhnify(s string) (string, error) { + if len(s) != 56 { + return "", fmt.Errorf("unsupported string length %d", len(s)) + } + + res := make([]string, 0, 4) + for i := 0; i < 4; i++ { + p := s[i*14 : (i+1)*14-1] + l, err := luhn.Base32.Generate(p) + if err != nil { + return "", err + } + if g := fmt.Sprintf("%s%c", p, l); g != s[i*14:(i+1)*14] { + return "", errors.New("check digit incorrect") + } + res = append(res, p) + } + return res[0] + res[1] + res[2] + res[3], nil +} + +func chunkify(s string) string { + s = regexp.MustCompile("(.{7})").ReplaceAllString(s, "$1-") + s = strings.Trim(s, "-") + return s +} + +func unchunkify(s string) string { + s = strings.Replace(s, "-", "", -1) + s = strings.Replace(s, " ", "", -1) + return s +} + +func untypeoify(s string) string { + s = strings.Replace(s, "0", "O", -1) + s = strings.Replace(s, "1", "I", -1) + s = strings.Replace(s, "8", "B", -1) + return s +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/deviceid_test.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/deviceid_test.go new file mode 100644 index 000000000..613557d32 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/deviceid_test.go @@ -0,0 +1,76 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import "testing" + +var formatted = "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2" +var formatCases = []string{ + "P56IOI-7MZJNU-2IQGDR-EYDM2M-GTMGL3-BXNPQ6-W5BTBB-Z4TJXZ-WICQ", + "P56IOI-7MZJNU2Y-IQGDR-EYDM2M-GTI-MGL3-BXNPQ6-W5BM-TBB-Z4TJXZ-WICQ2", + "P56IOI7 MZJNU2I QGDREYD M2MGTMGL 3BXNPQ6W 5BTB BZ4T JXZWICQ", + "P56IOI7 MZJNU2Y IQGDREY DM2MGTI MGL3BXN PQ6W5BM TBBZ4TJ XZWICQ2", + "P56IOI7MZJNU2IQGDREYDM2MGTMGL3BXNPQ6W5BTBBZ4TJXZWICQ", + "p56ioi7mzjnu2iqgdreydm2mgtmgl3bxnpq6w5btbbz4tjxzwicq", + "P56IOI7MZJNU2YIQGDREYDM2MGTIMGL3BXNPQ6W5BMTBBZ4TJXZWICQ2", + "P561017MZJNU2YIQGDREYDM2MGTIMGL3BXNPQ6W5BMT88Z4TJXZWICQ2", + "p56ioi7mzjnu2yiqgdreydm2mgtimgl3bxnpq6w5bmtbbz4tjxzwicq2", + "p561017mzjnu2yiqgdreydm2mgtimgl3bxnpq6w5bmt88z4tjxzwicq2", +} + +func TestFormatDeviceID(t *testing.T) { + for i, tc := range formatCases { + var id DeviceID + err := id.UnmarshalText([]byte(tc)) + if err != nil { + t.Errorf("#%d UnmarshalText(%q); %v", i, tc, err) + } else if f := id.String(); f != formatted { + t.Errorf("#%d FormatDeviceID(%q)\n\t%q !=\n\t%q", i, tc, f, formatted) + } + } +} + +var validateCases = []struct { + s string + ok bool +}{ + {"", false}, + {"P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2", true}, + {"P56IOI7-MZJNU2-IQGDREY-DM2MGT-MGL3BXN-PQ6W5B-TBBZ4TJ-XZWICQ", true}, + {"P56IOI7 MZJNU2I QGDREYD M2MGTMGL 3BXNPQ6W 5BTB BZ4T JXZWICQ", true}, + {"P56IOI7MZJNU2IQGDREYDM2MGTMGL3BXNPQ6W5BTBBZ4TJXZWICQ", true}, + {"P56IOI7MZJNU2IQGDREYDM2MGTMGL3BXNPQ6W5BTBBZ4TJXZWICQCCCC", false}, + {"p56ioi7mzjnu2iqgdreydm2mgtmgl3bxnpq6w5btbbz4tjxzwicq", true}, + {"p56ioi7mzjnu2iqgdreydm2mgtmgl3bxnpq6w5btbbz4tjxzwicqCCCC", false}, +} + +func TestValidateDeviceID(t *testing.T) { + for _, tc := range validateCases { + var id DeviceID + err := id.UnmarshalText([]byte(tc.s)) + if (err == nil && !tc.ok) || (err != nil && tc.ok) { + t.Errorf("ValidateDeviceID(%q); %v != %v", tc.s, err, tc.ok) + } + } +} + +func TestMarshallingDeviceID(t *testing.T) { + n0 := DeviceID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} + n1 := DeviceID{} + n2 := DeviceID{} + + bs, _ := n0.MarshalText() + n1.UnmarshalText(bs) + bs, _ = n1.MarshalText() + n2.UnmarshalText(bs) + + if n2.String() != n0.String() { + t.Errorf("String marshalling error; %q != %q", n2.String(), n0.String()) + } + if !n2.Equals(n0) { + t.Error("Equals error") + } + if n2.Compare(n0) != 0 { + t.Error("Compare error") + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/doc.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/doc.go new file mode 100644 index 000000000..2c6ea8ef2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/doc.go @@ -0,0 +1,4 @@ +// Copyright (C) 2014 The Protocol Authors. + +// Package protocol implements the Block Exchange Protocol. +package protocol diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/errors.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/errors.go new file mode 100644 index 000000000..31d27af0d --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/errors.go @@ -0,0 +1,51 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import ( + "errors" +) + +const ( + ecNoError int32 = iota + ecGeneric + ecNoSuchFile + ecInvalid +) + +var ( + ErrNoError error = nil + ErrGeneric = errors.New("generic error") + ErrNoSuchFile = errors.New("no such file") + ErrInvalid = errors.New("file is invalid") +) + +var lookupError = map[int32]error{ + ecNoError: ErrNoError, + ecGeneric: ErrGeneric, + ecNoSuchFile: ErrNoSuchFile, + ecInvalid: ErrInvalid, +} + +var lookupCode = map[error]int32{ + ErrNoError: ecNoError, + ErrGeneric: ecGeneric, + ErrNoSuchFile: ecNoSuchFile, + ErrInvalid: ecInvalid, +} + +func codeToError(errcode int32) error { + err, ok := lookupError[errcode] + if !ok { + return ErrGeneric + } + return err +} + +func errorToCode(err error) int32 { + code, ok := lookupCode[err] + if !ok { + return ecGeneric + } + return code +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/fuzz.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/fuzz.go new file mode 100644 index 000000000..9b82abe7c --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/fuzz.go @@ -0,0 +1,70 @@ +// Copyright (C) 2015 The Protocol Authors. + +// +build gofuzz + +package protocol + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" + "reflect" + "sync" +) + +func Fuzz(data []byte) int { + // Regenerate the length, or we'll most commonly exit quickly due to an + // unexpected eof which is unintestering. + if len(data) > 8 { + binary.BigEndian.PutUint32(data[4:], uint32(len(data))-8) + } + + // Setup a rawConnection we'll use to parse the message. + c := rawConnection{ + cr: &countingReader{Reader: bytes.NewReader(data)}, + closed: make(chan struct{}), + pool: sync.Pool{ + New: func() interface{} { + return make([]byte, BlockSize) + }, + }, + } + + // Attempt to parse the message. + hdr, msg, err := c.readMessage() + if err != nil { + return 0 + } + + // If parsing worked, attempt to encode it again. + newBs, err := msg.AppendXDR(nil) + if err != nil { + panic("not encodable") + } + + // Create an appriate header for the re-encoding. + newMsg := make([]byte, 8) + binary.BigEndian.PutUint32(newMsg, encodeHeader(hdr)) + binary.BigEndian.PutUint32(newMsg[4:], uint32(len(newBs))) + newMsg = append(newMsg, newBs...) + + // Use the rawConnection to parse the re-encoding. + c.cr = &countingReader{Reader: bytes.NewReader(newMsg)} + hdr2, msg2, err := c.readMessage() + if err != nil { + fmt.Println("Initial:\n" + hex.Dump(data)) + fmt.Println("New:\n" + hex.Dump(newMsg)) + panic("not parseable after re-encode: " + err.Error()) + } + + // Make sure the data is the same as it was before. + if hdr != hdr2 { + panic("headers differ") + } + if !reflect.DeepEqual(msg, msg2) { + panic("contents differ") + } + + return 1 +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/fuzz_test.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/fuzz_test.go new file mode 100644 index 000000000..65c2d9010 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/fuzz_test.go @@ -0,0 +1,89 @@ +// Copyright (C) 2015 The Protocol Authors. + +// +build gofuzz + +package protocol + +import ( + "encoding/binary" + "fmt" + "io/ioutil" + "os" + "strings" + "testing" + "testing/quick" +) + +// This can be used to generate a corpus of valid messages as a starting point +// for the fuzzer. +func TestGenerateCorpus(t *testing.T) { + t.Skip("Use to generate initial corpus only") + + n := 0 + check := func(idx IndexMessage) bool { + for i := range idx.Options { + if len(idx.Options[i].Key) > 64 { + idx.Options[i].Key = idx.Options[i].Key[:64] + } + } + hdr := header{ + version: 0, + msgID: 42, + msgType: messageTypeIndex, + compression: false, + } + + msgBs := idx.MustMarshalXDR() + + buf := make([]byte, 8) + binary.BigEndian.PutUint32(buf, encodeHeader(hdr)) + binary.BigEndian.PutUint32(buf[4:], uint32(len(msgBs))) + buf = append(buf, msgBs...) + + ioutil.WriteFile(fmt.Sprintf("testdata/corpus/test-%03d.xdr", n), buf, 0644) + n++ + return true + } + + if err := quick.Check(check, &quick.Config{MaxCount: 1000}); err != nil { + t.Fatal(err) + } +} + +// Tests any crashers found by the fuzzer, for closer investigation. +func TestCrashers(t *testing.T) { + testFiles(t, "testdata/crashers") +} + +// Tests the entire corpus, which should PASS before the fuzzer starts +// fuzzing. +func TestCorpus(t *testing.T) { + testFiles(t, "testdata/corpus") +} + +func testFiles(t *testing.T, dir string) { + fd, err := os.Open(dir) + if err != nil { + t.Fatal(err) + } + crashers, err := fd.Readdirnames(-1) + if err != nil { + t.Fatal(err) + } + for _, name := range crashers { + if strings.HasSuffix(name, ".output") { + continue + } + if strings.HasSuffix(name, ".quoted") { + continue + } + + t.Log(name) + crasher, err := ioutil.ReadFile(dir + "/" + name) + if err != nil { + t.Fatal(err) + } + + Fuzz(crasher) + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/header.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/header.go new file mode 100644 index 000000000..846ee48cd --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/header.go @@ -0,0 +1,43 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import "github.com/calmh/xdr" + +type header struct { + version int + msgID int + msgType int + compression bool +} + +func (h header) encodeXDR(xw *xdr.Writer) (int, error) { + u := encodeHeader(h) + return xw.WriteUint32(u) +} + +func (h *header) decodeXDR(xr *xdr.Reader) error { + u := xr.ReadUint32() + *h = decodeHeader(u) + return xr.Error() +} + +func encodeHeader(h header) uint32 { + var isComp uint32 + if h.compression { + isComp = 1 << 0 // the zeroth bit is the compression bit + } + return uint32(h.version&0xf)<<28 + + uint32(h.msgID&0xfff)<<16 + + uint32(h.msgType&0xff)<<8 + + isComp +} + +func decodeHeader(u uint32) header { + return header{ + version: int(u>>28) & 0xf, + msgID: int(u>>16) & 0xfff, + msgType: int(u>>8) & 0xff, + compression: u&1 == 1, + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/message.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/message.go new file mode 100644 index 000000000..2a37136b5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/message.go @@ -0,0 +1,152 @@ +// Copyright (C) 2014 The Protocol Authors. + +//go:generate -command genxdr go run ../syncthing/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go +//go:generate genxdr -o message_xdr.go message.go + +package protocol + +import "fmt" + +type IndexMessage struct { + Folder string + Files []FileInfo // max:1000000 + Flags uint32 + Options []Option // max:64 +} + +type FileInfo struct { + Name string // max:8192 + Flags uint32 + Modified int64 + Version Vector + LocalVersion int64 + CachedSize int64 // noencode (cache only) + Blocks []BlockInfo // max:1000000 +} + +func (f FileInfo) String() string { + return fmt.Sprintf("File{Name:%q, Flags:0%o, Modified:%d, Version:%v, Size:%d, Blocks:%v}", + f.Name, f.Flags, f.Modified, f.Version, f.Size(), f.Blocks) +} + +func (f FileInfo) Size() (bytes int64) { + if f.IsDeleted() || f.IsDirectory() { + return 128 + } + for _, b := range f.Blocks { + bytes += int64(b.Size) + } + return +} + +func (f FileInfo) IsDeleted() bool { + return f.Flags&FlagDeleted != 0 +} + +func (f FileInfo) IsInvalid() bool { + return f.Flags&FlagInvalid != 0 +} + +func (f FileInfo) IsDirectory() bool { + return f.Flags&FlagDirectory != 0 +} + +func (f FileInfo) IsSymlink() bool { + return f.Flags&FlagSymlink != 0 +} + +func (f FileInfo) HasPermissionBits() bool { + return f.Flags&FlagNoPermBits == 0 +} + +// WinsConflict returns true if "f" is the one to choose when it is in +// conflict with "other". +func (f FileInfo) WinsConflict(other FileInfo) bool { + // If a modification is in conflict with a delete, we pick the + // modification. + if !f.IsDeleted() && other.IsDeleted() { + return true + } + if f.IsDeleted() && !other.IsDeleted() { + return false + } + + // The one with the newer modification time wins. + if f.Modified > other.Modified { + return true + } + if f.Modified < other.Modified { + return false + } + + // The modification times were equal. Use the device ID in the version + // vector as tie breaker. + return f.Version.Compare(other.Version) == ConcurrentGreater +} + +type BlockInfo struct { + Offset int64 // noencode (cache only) + Size int32 + Hash []byte // max:64 +} + +func (b BlockInfo) String() string { + return fmt.Sprintf("Block{%d/%d/%x}", b.Offset, b.Size, b.Hash) +} + +type RequestMessage struct { + Folder string // max:64 + Name string // max:8192 + Offset int64 + Size int32 + Hash []byte // max:64 + Flags uint32 + Options []Option // max:64 +} + +type ResponseMessage struct { + Data []byte + Code int32 +} + +type ClusterConfigMessage struct { + ClientName string // max:64 + ClientVersion string // max:64 + Folders []Folder // max:1000000 + Options []Option // max:64 +} + +func (o *ClusterConfigMessage) GetOption(key string) string { + for _, option := range o.Options { + if option.Key == key { + return option.Value + } + } + return "" +} + +type Folder struct { + ID string // max:64 + Devices []Device // max:1000000 + Flags uint32 + Options []Option // max:64 +} + +type Device struct { + ID []byte // max:32 + MaxLocalVersion int64 + Flags uint32 + Options []Option // max:64 +} + +type Option struct { + Key string // max:64 + Value string // max:1024 +} + +type CloseMessage struct { + Reason string // max:1024 + Code int32 +} + +type EmptyMessage struct{} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/message_xdr.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/message_xdr.go new file mode 100644 index 000000000..876fbb77c --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/message_xdr.go @@ -0,0 +1,1136 @@ +// ************************************************************ +// This file is automatically generated by genxdr. Do not edit. +// ************************************************************ + +package protocol + +import ( + "bytes" + "io" + + "github.com/calmh/xdr" +) + +/* + +IndexMessage Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Folder | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Folder (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of Files | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Zero or more FileInfo Structures \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Flags | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of Options | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Zero or more Option Structures \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct IndexMessage { + string Folder<>; + FileInfo Files<1000000>; + unsigned int Flags; + Option Options<64>; +} + +*/ + +func (o IndexMessage) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o IndexMessage) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o IndexMessage) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o IndexMessage) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o IndexMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { + xw.WriteString(o.Folder) + if l := len(o.Files); l > 1000000 { + return xw.Tot(), xdr.ElementSizeExceeded("Files", l, 1000000) + } + xw.WriteUint32(uint32(len(o.Files))) + for i := range o.Files { + _, err := o.Files[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + } + xw.WriteUint32(o.Flags) + if l := len(o.Options); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64) + } + xw.WriteUint32(uint32(len(o.Options))) + for i := range o.Options { + _, err := o.Options[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + } + return xw.Tot(), xw.Error() +} + +func (o *IndexMessage) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *IndexMessage) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *IndexMessage) DecodeXDRFrom(xr *xdr.Reader) error { + o.Folder = xr.ReadString() + _FilesSize := int(xr.ReadUint32()) + if _FilesSize < 0 { + return xdr.ElementSizeExceeded("Files", _FilesSize, 1000000) + } + if _FilesSize > 1000000 { + return xdr.ElementSizeExceeded("Files", _FilesSize, 1000000) + } + o.Files = make([]FileInfo, _FilesSize) + for i := range o.Files { + (&o.Files[i]).DecodeXDRFrom(xr) + } + o.Flags = xr.ReadUint32() + _OptionsSize := int(xr.ReadUint32()) + if _OptionsSize < 0 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + if _OptionsSize > 64 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + o.Options = make([]Option, _OptionsSize) + for i := range o.Options { + (&o.Options[i]).DecodeXDRFrom(xr) + } + return xr.Error() +} + +/* + +FileInfo Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Name | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Name (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Flags | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++ Modified (64 bits) + +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Vector Structure \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++ Local Version (64 bits) + +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of Blocks | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Zero or more BlockInfo Structures \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct FileInfo { + string Name<8192>; + unsigned int Flags; + hyper Modified; + Vector Version; + hyper LocalVersion; + BlockInfo Blocks<1000000>; +} + +*/ + +func (o FileInfo) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o FileInfo) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o FileInfo) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o FileInfo) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o FileInfo) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.Name); l > 8192 { + return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192) + } + xw.WriteString(o.Name) + xw.WriteUint32(o.Flags) + xw.WriteUint64(uint64(o.Modified)) + _, err := o.Version.EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + xw.WriteUint64(uint64(o.LocalVersion)) + if l := len(o.Blocks); l > 1000000 { + return xw.Tot(), xdr.ElementSizeExceeded("Blocks", l, 1000000) + } + xw.WriteUint32(uint32(len(o.Blocks))) + for i := range o.Blocks { + _, err := o.Blocks[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + } + return xw.Tot(), xw.Error() +} + +func (o *FileInfo) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *FileInfo) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *FileInfo) DecodeXDRFrom(xr *xdr.Reader) error { + o.Name = xr.ReadStringMax(8192) + o.Flags = xr.ReadUint32() + o.Modified = int64(xr.ReadUint64()) + (&o.Version).DecodeXDRFrom(xr) + o.LocalVersion = int64(xr.ReadUint64()) + _BlocksSize := int(xr.ReadUint32()) + if _BlocksSize < 0 { + return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 1000000) + } + if _BlocksSize > 1000000 { + return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 1000000) + } + o.Blocks = make([]BlockInfo, _BlocksSize) + for i := range o.Blocks { + (&o.Blocks[i]).DecodeXDRFrom(xr) + } + return xr.Error() +} + +/* + +BlockInfo Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Hash | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Hash (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct BlockInfo { + int Size; + opaque Hash<64>; +} + +*/ + +func (o BlockInfo) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o BlockInfo) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o BlockInfo) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o BlockInfo) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o BlockInfo) EncodeXDRInto(xw *xdr.Writer) (int, error) { + xw.WriteUint32(uint32(o.Size)) + if l := len(o.Hash); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("Hash", l, 64) + } + xw.WriteBytes(o.Hash) + return xw.Tot(), xw.Error() +} + +func (o *BlockInfo) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *BlockInfo) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *BlockInfo) DecodeXDRFrom(xr *xdr.Reader) error { + o.Size = int32(xr.ReadUint32()) + o.Hash = xr.ReadBytesMax(64) + return xr.Error() +} + +/* + +RequestMessage Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Folder | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Folder (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Name | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Name (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++ Offset (64 bits) + +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Size | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Hash | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Hash (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Flags | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of Options | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Zero or more Option Structures \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct RequestMessage { + string Folder<64>; + string Name<8192>; + hyper Offset; + int Size; + opaque Hash<64>; + unsigned int Flags; + Option Options<64>; +} + +*/ + +func (o RequestMessage) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o RequestMessage) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o RequestMessage) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o RequestMessage) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o RequestMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.Folder); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("Folder", l, 64) + } + xw.WriteString(o.Folder) + if l := len(o.Name); l > 8192 { + return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192) + } + xw.WriteString(o.Name) + xw.WriteUint64(uint64(o.Offset)) + xw.WriteUint32(uint32(o.Size)) + if l := len(o.Hash); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("Hash", l, 64) + } + xw.WriteBytes(o.Hash) + xw.WriteUint32(o.Flags) + if l := len(o.Options); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64) + } + xw.WriteUint32(uint32(len(o.Options))) + for i := range o.Options { + _, err := o.Options[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + } + return xw.Tot(), xw.Error() +} + +func (o *RequestMessage) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *RequestMessage) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *RequestMessage) DecodeXDRFrom(xr *xdr.Reader) error { + o.Folder = xr.ReadStringMax(64) + o.Name = xr.ReadStringMax(8192) + o.Offset = int64(xr.ReadUint64()) + o.Size = int32(xr.ReadUint32()) + o.Hash = xr.ReadBytesMax(64) + o.Flags = xr.ReadUint32() + _OptionsSize := int(xr.ReadUint32()) + if _OptionsSize < 0 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + if _OptionsSize > 64 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + o.Options = make([]Option, _OptionsSize) + for i := range o.Options { + (&o.Options[i]).DecodeXDRFrom(xr) + } + return xr.Error() +} + +/* + +ResponseMessage Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Data | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Data (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Code | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct ResponseMessage { + opaque Data<>; + int Code; +} + +*/ + +func (o ResponseMessage) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o ResponseMessage) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o ResponseMessage) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o ResponseMessage) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o ResponseMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { + xw.WriteBytes(o.Data) + xw.WriteUint32(uint32(o.Code)) + return xw.Tot(), xw.Error() +} + +func (o *ResponseMessage) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *ResponseMessage) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *ResponseMessage) DecodeXDRFrom(xr *xdr.Reader) error { + o.Data = xr.ReadBytes() + o.Code = int32(xr.ReadUint32()) + return xr.Error() +} + +/* + +ClusterConfigMessage Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Client Name | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Client Name (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Client Version | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Client Version (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of Folders | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Zero or more Folder Structures \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of Options | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Zero or more Option Structures \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct ClusterConfigMessage { + string ClientName<64>; + string ClientVersion<64>; + Folder Folders<1000000>; + Option Options<64>; +} + +*/ + +func (o ClusterConfigMessage) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o ClusterConfigMessage) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o ClusterConfigMessage) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o ClusterConfigMessage) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o ClusterConfigMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.ClientName); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("ClientName", l, 64) + } + xw.WriteString(o.ClientName) + if l := len(o.ClientVersion); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("ClientVersion", l, 64) + } + xw.WriteString(o.ClientVersion) + if l := len(o.Folders); l > 1000000 { + return xw.Tot(), xdr.ElementSizeExceeded("Folders", l, 1000000) + } + xw.WriteUint32(uint32(len(o.Folders))) + for i := range o.Folders { + _, err := o.Folders[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + } + if l := len(o.Options); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64) + } + xw.WriteUint32(uint32(len(o.Options))) + for i := range o.Options { + _, err := o.Options[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + } + return xw.Tot(), xw.Error() +} + +func (o *ClusterConfigMessage) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *ClusterConfigMessage) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *ClusterConfigMessage) DecodeXDRFrom(xr *xdr.Reader) error { + o.ClientName = xr.ReadStringMax(64) + o.ClientVersion = xr.ReadStringMax(64) + _FoldersSize := int(xr.ReadUint32()) + if _FoldersSize < 0 { + return xdr.ElementSizeExceeded("Folders", _FoldersSize, 1000000) + } + if _FoldersSize > 1000000 { + return xdr.ElementSizeExceeded("Folders", _FoldersSize, 1000000) + } + o.Folders = make([]Folder, _FoldersSize) + for i := range o.Folders { + (&o.Folders[i]).DecodeXDRFrom(xr) + } + _OptionsSize := int(xr.ReadUint32()) + if _OptionsSize < 0 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + if _OptionsSize > 64 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + o.Options = make([]Option, _OptionsSize) + for i := range o.Options { + (&o.Options[i]).DecodeXDRFrom(xr) + } + return xr.Error() +} + +/* + +Folder Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of ID | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ ID (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of Devices | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Zero or more Device Structures \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Flags | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of Options | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Zero or more Option Structures \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct Folder { + string ID<64>; + Device Devices<1000000>; + unsigned int Flags; + Option Options<64>; +} + +*/ + +func (o Folder) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o Folder) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o Folder) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o Folder) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o Folder) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.ID); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 64) + } + xw.WriteString(o.ID) + if l := len(o.Devices); l > 1000000 { + return xw.Tot(), xdr.ElementSizeExceeded("Devices", l, 1000000) + } + xw.WriteUint32(uint32(len(o.Devices))) + for i := range o.Devices { + _, err := o.Devices[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + } + xw.WriteUint32(o.Flags) + if l := len(o.Options); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64) + } + xw.WriteUint32(uint32(len(o.Options))) + for i := range o.Options { + _, err := o.Options[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + } + return xw.Tot(), xw.Error() +} + +func (o *Folder) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *Folder) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *Folder) DecodeXDRFrom(xr *xdr.Reader) error { + o.ID = xr.ReadStringMax(64) + _DevicesSize := int(xr.ReadUint32()) + if _DevicesSize < 0 { + return xdr.ElementSizeExceeded("Devices", _DevicesSize, 1000000) + } + if _DevicesSize > 1000000 { + return xdr.ElementSizeExceeded("Devices", _DevicesSize, 1000000) + } + o.Devices = make([]Device, _DevicesSize) + for i := range o.Devices { + (&o.Devices[i]).DecodeXDRFrom(xr) + } + o.Flags = xr.ReadUint32() + _OptionsSize := int(xr.ReadUint32()) + if _OptionsSize < 0 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + if _OptionsSize > 64 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + o.Options = make([]Option, _OptionsSize) + for i := range o.Options { + (&o.Options[i]).DecodeXDRFrom(xr) + } + return xr.Error() +} + +/* + +Device Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of ID | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ ID (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++ Max Local Version (64 bits) + +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Flags | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of Options | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Zero or more Option Structures \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct Device { + opaque ID<32>; + hyper MaxLocalVersion; + unsigned int Flags; + Option Options<64>; +} + +*/ + +func (o Device) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o Device) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o Device) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o Device) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o Device) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.ID); l > 32 { + return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 32) + } + xw.WriteBytes(o.ID) + xw.WriteUint64(uint64(o.MaxLocalVersion)) + xw.WriteUint32(o.Flags) + if l := len(o.Options); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64) + } + xw.WriteUint32(uint32(len(o.Options))) + for i := range o.Options { + _, err := o.Options[i].EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } + } + return xw.Tot(), xw.Error() +} + +func (o *Device) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *Device) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *Device) DecodeXDRFrom(xr *xdr.Reader) error { + o.ID = xr.ReadBytesMax(32) + o.MaxLocalVersion = int64(xr.ReadUint64()) + o.Flags = xr.ReadUint32() + _OptionsSize := int(xr.ReadUint32()) + if _OptionsSize < 0 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + if _OptionsSize > 64 { + return xdr.ElementSizeExceeded("Options", _OptionsSize, 64) + } + o.Options = make([]Option, _OptionsSize) + for i := range o.Options { + (&o.Options[i]).DecodeXDRFrom(xr) + } + return xr.Error() +} + +/* + +Option Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Key | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Key (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Value | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Value (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct Option { + string Key<64>; + string Value<1024>; +} + +*/ + +func (o Option) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o Option) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o Option) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o Option) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o Option) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.Key); l > 64 { + return xw.Tot(), xdr.ElementSizeExceeded("Key", l, 64) + } + xw.WriteString(o.Key) + if l := len(o.Value); l > 1024 { + return xw.Tot(), xdr.ElementSizeExceeded("Value", l, 1024) + } + xw.WriteString(o.Value) + return xw.Tot(), xw.Error() +} + +func (o *Option) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *Option) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *Option) DecodeXDRFrom(xr *xdr.Reader) error { + o.Key = xr.ReadStringMax(64) + o.Value = xr.ReadStringMax(1024) + return xr.Error() +} + +/* + +CloseMessage Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length of Reason | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/ / +\ Reason (variable length) \ +/ / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Code | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct CloseMessage { + string Reason<1024>; + int Code; +} + +*/ + +func (o CloseMessage) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o CloseMessage) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o CloseMessage) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o CloseMessage) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o CloseMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { + if l := len(o.Reason); l > 1024 { + return xw.Tot(), xdr.ElementSizeExceeded("Reason", l, 1024) + } + xw.WriteString(o.Reason) + xw.WriteUint32(uint32(o.Code)) + return xw.Tot(), xw.Error() +} + +func (o *CloseMessage) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *CloseMessage) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *CloseMessage) DecodeXDRFrom(xr *xdr.Reader) error { + o.Reason = xr.ReadStringMax(1024) + o.Code = int32(xr.ReadUint32()) + return xr.Error() +} + +/* + +EmptyMessage Structure: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +struct EmptyMessage { +} + +*/ + +func (o EmptyMessage) EncodeXDR(w io.Writer) (int, error) { + var xw = xdr.NewWriter(w) + return o.EncodeXDRInto(xw) +} + +func (o EmptyMessage) MarshalXDR() ([]byte, error) { + return o.AppendXDR(make([]byte, 0, 128)) +} + +func (o EmptyMessage) MustMarshalXDR() []byte { + bs, err := o.MarshalXDR() + if err != nil { + panic(err) + } + return bs +} + +func (o EmptyMessage) AppendXDR(bs []byte) ([]byte, error) { + var aw = xdr.AppendWriter(bs) + var xw = xdr.NewWriter(&aw) + _, err := o.EncodeXDRInto(xw) + return []byte(aw), err +} + +func (o EmptyMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { + return xw.Tot(), xw.Error() +} + +func (o *EmptyMessage) DecodeXDR(r io.Reader) error { + xr := xdr.NewReader(r) + return o.DecodeXDRFrom(xr) +} + +func (o *EmptyMessage) UnmarshalXDR(bs []byte) error { + var br = bytes.NewReader(bs) + var xr = xdr.NewReader(br) + return o.DecodeXDRFrom(xr) +} + +func (o *EmptyMessage) DecodeXDRFrom(xr *xdr.Reader) error { + return xr.Error() +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/nativemodel_darwin.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/nativemodel_darwin.go new file mode 100644 index 000000000..eb755a6e4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/nativemodel_darwin.go @@ -0,0 +1,40 @@ +// Copyright (C) 2014 The Protocol Authors. + +// +build darwin + +package protocol + +// Darwin uses NFD normalization + +import "golang.org/x/text/unicode/norm" + +type nativeModel struct { + next Model +} + +func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) { + for i := range files { + files[i].Name = norm.NFD.String(files[i].Name) + } + m.next.Index(deviceID, folder, files, flags, options) +} + +func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) { + for i := range files { + files[i].Name = norm.NFD.String(files[i].Name) + } + m.next.IndexUpdate(deviceID, folder, files, flags, options) +} + +func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error { + name = norm.NFD.String(name) + return m.next.Request(deviceID, folder, name, offset, hash, flags, options, buf) +} + +func (m nativeModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) { + m.next.ClusterConfig(deviceID, config) +} + +func (m nativeModel) Close(deviceID DeviceID, err error) { + m.next.Close(deviceID, err) +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/nativemodel_unix.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/nativemodel_unix.go new file mode 100644 index 000000000..0611865e1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/nativemodel_unix.go @@ -0,0 +1,31 @@ +// Copyright (C) 2014 The Protocol Authors. + +// +build !windows,!darwin + +package protocol + +// Normal Unixes uses NFC and slashes, which is the wire format. + +type nativeModel struct { + next Model +} + +func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) { + m.next.Index(deviceID, folder, files, flags, options) +} + +func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) { + m.next.IndexUpdate(deviceID, folder, files, flags, options) +} + +func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error { + return m.next.Request(deviceID, folder, name, offset, hash, flags, options, buf) +} + +func (m nativeModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) { + m.next.ClusterConfig(deviceID, config) +} + +func (m nativeModel) Close(deviceID DeviceID, err error) { + m.next.Close(deviceID, err) +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/nativemodel_windows.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/nativemodel_windows.go new file mode 100644 index 000000000..36a1d2749 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/nativemodel_windows.go @@ -0,0 +1,63 @@ +// Copyright (C) 2014 The Protocol Authors. + +// +build windows + +package protocol + +// Windows uses backslashes as file separator and disallows a bunch of +// characters in the filename + +import ( + "path/filepath" + "strings" +) + +var disallowedCharacters = string([]rune{ + '<', '>', ':', '"', '|', '?', '*', + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, +}) + +type nativeModel struct { + next Model +} + +func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) { + fixupFiles(folder, files) + m.next.Index(deviceID, folder, files, flags, options) +} + +func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) { + fixupFiles(folder, files) + m.next.IndexUpdate(deviceID, folder, files, flags, options) +} + +func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error { + name = filepath.FromSlash(name) + return m.next.Request(deviceID, folder, name, offset, hash, flags, options, buf) +} + +func (m nativeModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) { + m.next.ClusterConfig(deviceID, config) +} + +func (m nativeModel) Close(deviceID DeviceID, err error) { + m.next.Close(deviceID, err) +} + +func fixupFiles(folder string, files []FileInfo) { + for i, f := range files { + if strings.ContainsAny(f.Name, disallowedCharacters) { + if f.IsDeleted() { + // Don't complain if the file is marked as deleted, since it + // can't possibly exist here anyway. + continue + } + files[i].Flags |= FlagInvalid + l.Warnf("File name %q (folder %q) contains invalid characters; marked as invalid.", f.Name, folder) + } + files[i].Name = filepath.FromSlash(files[i].Name) + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/protocol.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/protocol.go new file mode 100644 index 000000000..4c1364eaf --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/protocol.go @@ -0,0 +1,782 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import ( + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "io" + "sync" + "time" + + lz4 "github.com/bkaradzic/go-lz4" +) + +const ( + // Data block size (128 KiB) + BlockSize = 128 << 10 + + // We reject messages larger than this when encountered on the wire. (64 MiB) + MaxMessageLen = 64 << 20 +) + +const ( + messageTypeClusterConfig = 0 + messageTypeIndex = 1 + messageTypeRequest = 2 + messageTypeResponse = 3 + messageTypePing = 4 + messageTypeIndexUpdate = 6 + messageTypeClose = 7 +) + +const ( + stateInitial = iota + stateReady +) + +// FileInfo flags +const ( + FlagDeleted uint32 = 1 << 12 + FlagInvalid = 1 << 13 + FlagDirectory = 1 << 14 + FlagNoPermBits = 1 << 15 + FlagSymlink = 1 << 16 + FlagSymlinkMissingTarget = 1 << 17 + + FlagsAll = (1 << 18) - 1 + + SymlinkTypeMask = FlagDirectory | FlagSymlinkMissingTarget +) + +// IndexMessage message flags (for IndexUpdate) +const ( + FlagIndexTemporary uint32 = 1 << iota +) + +// Request message flags +const ( + FlagRequestTemporary uint32 = 1 << iota +) + +// ClusterConfigMessage.Folders.Devices flags +const ( + FlagShareTrusted uint32 = 1 << 0 + FlagShareReadOnly = 1 << 1 + FlagIntroducer = 1 << 2 + FlagShareBits = 0x000000ff +) + +var ( + ErrClosed = errors.New("connection closed") + ErrTimeout = errors.New("read timeout") +) + +// Specific variants of empty messages... +type pingMessage struct{ EmptyMessage } + +type Model interface { + // An index was received from the peer device + Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) + // An index update was received from the peer device + IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) + // A request was made by the peer device + Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error + // A cluster configuration message was received + ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) + // The peer device closed the connection + Close(deviceID DeviceID, err error) +} + +type Connection interface { + Start() + ID() DeviceID + Name() string + Index(folder string, files []FileInfo, flags uint32, options []Option) error + IndexUpdate(folder string, files []FileInfo, flags uint32, options []Option) error + Request(folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) + ClusterConfig(config ClusterConfigMessage) + Statistics() Statistics +} + +type rawConnection struct { + id DeviceID + name string + receiver Model + + cr *countingReader + cw *countingWriter + + awaiting [4096]chan asyncResult + awaitingMut sync.Mutex + + idxMut sync.Mutex // ensures serialization of Index calls + + nextID chan int + outbox chan hdrMsg + closed chan struct{} + once sync.Once + pool sync.Pool + compression Compression + + rdbuf0 []byte // used & reused by readMessage + rdbuf1 []byte // used & reused by readMessage +} + +type asyncResult struct { + val []byte + err error +} + +type hdrMsg struct { + hdr header + msg encodable + done chan struct{} +} + +type encodable interface { + AppendXDR([]byte) ([]byte, error) +} + +type isEofer interface { + IsEOF() bool +} + +const ( + // We make sure to send a message at least this often, by triggering pings. + PingSendInterval = 90 * time.Second + // If we haven't received a message from the other side for this long, close the connection. + ReceiveTimeout = 300 * time.Second +) + +func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiver Model, name string, compress Compression) Connection { + cr := &countingReader{Reader: reader} + cw := &countingWriter{Writer: writer} + + c := rawConnection{ + id: deviceID, + name: name, + receiver: nativeModel{receiver}, + cr: cr, + cw: cw, + outbox: make(chan hdrMsg), + nextID: make(chan int), + closed: make(chan struct{}), + pool: sync.Pool{ + New: func() interface{} { + return make([]byte, BlockSize) + }, + }, + compression: compress, + } + + return wireFormatConnection{&c} +} + +// Start creates the goroutines for sending and receiving of messages. It must +// be called exactly once after creating a connection. +func (c *rawConnection) Start() { + go c.readerLoop() + go c.writerLoop() + go c.pingSender() + go c.pingReceiver() + go c.idGenerator() +} + +func (c *rawConnection) ID() DeviceID { + return c.id +} + +func (c *rawConnection) Name() string { + return c.name +} + +// Index writes the list of file information to the connected peer device +func (c *rawConnection) Index(folder string, idx []FileInfo, flags uint32, options []Option) error { + select { + case <-c.closed: + return ErrClosed + default: + } + c.idxMut.Lock() + c.send(-1, messageTypeIndex, IndexMessage{ + Folder: folder, + Files: idx, + Flags: flags, + Options: options, + }, nil) + c.idxMut.Unlock() + return nil +} + +// IndexUpdate writes the list of file information to the connected peer device as an update +func (c *rawConnection) IndexUpdate(folder string, idx []FileInfo, flags uint32, options []Option) error { + select { + case <-c.closed: + return ErrClosed + default: + } + c.idxMut.Lock() + c.send(-1, messageTypeIndexUpdate, IndexMessage{ + Folder: folder, + Files: idx, + Flags: flags, + Options: options, + }, nil) + c.idxMut.Unlock() + return nil +} + +// Request returns the bytes for the specified block after fetching them from the connected peer. +func (c *rawConnection) Request(folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) { + var id int + select { + case id = <-c.nextID: + case <-c.closed: + return nil, ErrClosed + } + + c.awaitingMut.Lock() + if ch := c.awaiting[id]; ch != nil { + panic("id taken") + } + rc := make(chan asyncResult, 1) + c.awaiting[id] = rc + c.awaitingMut.Unlock() + + ok := c.send(id, messageTypeRequest, RequestMessage{ + Folder: folder, + Name: name, + Offset: offset, + Size: int32(size), + Hash: hash, + Flags: flags, + Options: options, + }, nil) + if !ok { + return nil, ErrClosed + } + + res, ok := <-rc + if !ok { + return nil, ErrClosed + } + return res.val, res.err +} + +// ClusterConfig send the cluster configuration message to the peer and returns any error +func (c *rawConnection) ClusterConfig(config ClusterConfigMessage) { + c.send(-1, messageTypeClusterConfig, config, nil) +} + +func (c *rawConnection) ping() bool { + var id int + select { + case id = <-c.nextID: + case <-c.closed: + return false + } + + return c.send(id, messageTypePing, nil, nil) +} + +func (c *rawConnection) readerLoop() (err error) { + defer func() { + c.close(err) + }() + + state := stateInitial + for { + select { + case <-c.closed: + return ErrClosed + default: + } + + hdr, msg, err := c.readMessage() + if err != nil { + return err + } + + switch msg := msg.(type) { + case ClusterConfigMessage: + if state != stateInitial { + return fmt.Errorf("protocol error: cluster config message in state %d", state) + } + go c.receiver.ClusterConfig(c.id, msg) + state = stateReady + + case IndexMessage: + switch hdr.msgType { + case messageTypeIndex: + if state != stateReady { + return fmt.Errorf("protocol error: index message in state %d", state) + } + c.handleIndex(msg) + state = stateReady + + case messageTypeIndexUpdate: + if state != stateReady { + return fmt.Errorf("protocol error: index update message in state %d", state) + } + c.handleIndexUpdate(msg) + state = stateReady + } + + case RequestMessage: + if state != stateReady { + return fmt.Errorf("protocol error: request message in state %d", state) + } + // Requests are handled asynchronously + go c.handleRequest(hdr.msgID, msg) + + case ResponseMessage: + if state != stateReady { + return fmt.Errorf("protocol error: response message in state %d", state) + } + c.handleResponse(hdr.msgID, msg) + + case pingMessage: + if state != stateReady { + return fmt.Errorf("protocol error: ping message in state %d", state) + } + // Nothing + + case CloseMessage: + return errors.New(msg.Reason) + + default: + return fmt.Errorf("protocol error: %s: unknown message type %#x", c.id, hdr.msgType) + } + } +} + +func (c *rawConnection) readMessage() (hdr header, msg encodable, err error) { + if cap(c.rdbuf0) < 8 { + c.rdbuf0 = make([]byte, 8) + } else { + c.rdbuf0 = c.rdbuf0[:8] + } + _, err = io.ReadFull(c.cr, c.rdbuf0) + if err != nil { + return + } + + hdr = decodeHeader(binary.BigEndian.Uint32(c.rdbuf0[0:4])) + msglen := int(binary.BigEndian.Uint32(c.rdbuf0[4:8])) + + if debug { + l.Debugf("read header %v (msglen=%d)", hdr, msglen) + } + + if msglen > MaxMessageLen { + err = fmt.Errorf("message length %d exceeds maximum %d", msglen, MaxMessageLen) + return + } + + if hdr.version != 0 { + err = fmt.Errorf("unknown protocol version 0x%x", hdr.version) + return + } + + if cap(c.rdbuf0) < msglen { + c.rdbuf0 = make([]byte, msglen) + } else { + c.rdbuf0 = c.rdbuf0[:msglen] + } + _, err = io.ReadFull(c.cr, c.rdbuf0) + if err != nil { + return + } + + if debug { + l.Debugf("read %d bytes", len(c.rdbuf0)) + } + + msgBuf := c.rdbuf0 + if hdr.compression && msglen > 0 { + c.rdbuf1 = c.rdbuf1[:cap(c.rdbuf1)] + c.rdbuf1, err = lz4.Decode(c.rdbuf1, c.rdbuf0) + if err != nil { + return + } + msgBuf = c.rdbuf1 + if debug { + l.Debugf("decompressed to %d bytes", len(msgBuf)) + } + } + + if debug { + if len(msgBuf) > 1024 { + l.Debugf("message data:\n%s", hex.Dump(msgBuf[:1024])) + } else { + l.Debugf("message data:\n%s", hex.Dump(msgBuf)) + } + } + + // We check each returned error for the XDRError.IsEOF() method. + // IsEOF()==true here means that the message contained fewer fields than + // expected. It does not signify an EOF on the socket, because we've + // successfully read a size value and that many bytes already. New fields + // we expected but the other peer didn't send should be interpreted as + // zero/nil, and if that's not valid we'll verify it somewhere else. + + switch hdr.msgType { + case messageTypeIndex, messageTypeIndexUpdate: + var idx IndexMessage + err = idx.UnmarshalXDR(msgBuf) + if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() { + err = nil + } + msg = idx + + case messageTypeRequest: + var req RequestMessage + err = req.UnmarshalXDR(msgBuf) + if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() { + err = nil + } + msg = req + + case messageTypeResponse: + var resp ResponseMessage + err = resp.UnmarshalXDR(msgBuf) + if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() { + err = nil + } + msg = resp + + case messageTypePing: + msg = pingMessage{} + + case messageTypeClusterConfig: + var cc ClusterConfigMessage + err = cc.UnmarshalXDR(msgBuf) + if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() { + err = nil + } + msg = cc + + case messageTypeClose: + var cm CloseMessage + err = cm.UnmarshalXDR(msgBuf) + if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() { + err = nil + } + msg = cm + + default: + err = fmt.Errorf("protocol error: %s: unknown message type %#x", c.id, hdr.msgType) + } + + return +} + +func (c *rawConnection) handleIndex(im IndexMessage) { + if debug { + l.Debugf("Index(%v, %v, %d file, flags %x, opts: %s)", c.id, im.Folder, len(im.Files), im.Flags, im.Options) + } + c.receiver.Index(c.id, im.Folder, filterIndexMessageFiles(im.Files), im.Flags, im.Options) +} + +func (c *rawConnection) handleIndexUpdate(im IndexMessage) { + if debug { + l.Debugf("queueing IndexUpdate(%v, %v, %d files, flags %x, opts: %s)", c.id, im.Folder, len(im.Files), im.Flags, im.Options) + } + c.receiver.IndexUpdate(c.id, im.Folder, filterIndexMessageFiles(im.Files), im.Flags, im.Options) +} + +func filterIndexMessageFiles(fs []FileInfo) []FileInfo { + var out []FileInfo + for i, f := range fs { + switch f.Name { + case "", ".", "..", "/": // A few obviously invalid filenames + l.Infof("Dropping invalid filename %q from incoming index", f.Name) + if out == nil { + // Most incoming updates won't contain anything invalid, so we + // delay the allocation and copy to output slice until we + // really need to do it, then copy all the so var valid files + // to it. + out = make([]FileInfo, i, len(fs)-1) + copy(out, fs) + } + default: + if out != nil { + out = append(out, f) + } + } + } + if out != nil { + return out + } + return fs +} + +func (c *rawConnection) handleRequest(msgID int, req RequestMessage) { + size := int(req.Size) + usePool := size <= BlockSize + + var buf []byte + var done chan struct{} + + if usePool { + buf = c.pool.Get().([]byte)[:size] + done = make(chan struct{}) + } else { + buf = make([]byte, size) + } + + err := c.receiver.Request(c.id, req.Folder, req.Name, int64(req.Offset), req.Hash, req.Flags, req.Options, buf) + if err != nil { + c.send(msgID, messageTypeResponse, ResponseMessage{ + Data: nil, + Code: errorToCode(err), + }, done) + } else { + c.send(msgID, messageTypeResponse, ResponseMessage{ + Data: buf, + Code: errorToCode(err), + }, done) + } + + if usePool { + <-done + c.pool.Put(buf) + } +} + +func (c *rawConnection) handleResponse(msgID int, resp ResponseMessage) { + c.awaitingMut.Lock() + if rc := c.awaiting[msgID]; rc != nil { + c.awaiting[msgID] = nil + rc <- asyncResult{resp.Data, codeToError(resp.Code)} + close(rc) + } + c.awaitingMut.Unlock() +} + +func (c *rawConnection) handlePong(msgID int) { + c.awaitingMut.Lock() + if rc := c.awaiting[msgID]; rc != nil { + c.awaiting[msgID] = nil + rc <- asyncResult{} + close(rc) + } + c.awaitingMut.Unlock() +} + +func (c *rawConnection) send(msgID int, msgType int, msg encodable, done chan struct{}) bool { + if msgID < 0 { + select { + case id := <-c.nextID: + msgID = id + case <-c.closed: + return false + } + } + + hdr := header{ + version: 0, + msgID: msgID, + msgType: msgType, + } + + select { + case c.outbox <- hdrMsg{hdr, msg, done}: + return true + case <-c.closed: + return false + } +} + +func (c *rawConnection) writerLoop() { + var msgBuf = make([]byte, 8) // buffer for wire format message, kept and reused + var uncBuf []byte // buffer for uncompressed message, kept and reused + for { + var tempBuf []byte + var err error + + select { + case hm := <-c.outbox: + if hm.msg != nil { + // Uncompressed message in uncBuf + uncBuf, err = hm.msg.AppendXDR(uncBuf[:0]) + if hm.done != nil { + close(hm.done) + } + if err != nil { + c.close(err) + return + } + + compress := false + switch c.compression { + case CompressAlways: + compress = true + case CompressMetadata: + compress = hm.hdr.msgType != messageTypeResponse + } + + if compress && len(uncBuf) >= compressionThreshold { + // Use compression for large messages + hm.hdr.compression = true + + // Make sure we have enough space for the compressed message plus header in msgBug + msgBuf = msgBuf[:cap(msgBuf)] + if maxLen := lz4.CompressBound(len(uncBuf)) + 8; maxLen > len(msgBuf) { + msgBuf = make([]byte, maxLen) + } + + // Compressed is written to msgBuf, we keep tb for the length only + tempBuf, err = lz4.Encode(msgBuf[8:], uncBuf) + binary.BigEndian.PutUint32(msgBuf[4:8], uint32(len(tempBuf))) + msgBuf = msgBuf[0 : len(tempBuf)+8] + + if debug { + l.Debugf("write compressed message; %v (len=%d)", hm.hdr, len(tempBuf)) + } + } else { + // No point in compressing very short messages + hm.hdr.compression = false + + msgBuf = msgBuf[:cap(msgBuf)] + if l := len(uncBuf) + 8; l > len(msgBuf) { + msgBuf = make([]byte, l) + } + + binary.BigEndian.PutUint32(msgBuf[4:8], uint32(len(uncBuf))) + msgBuf = msgBuf[0 : len(uncBuf)+8] + copy(msgBuf[8:], uncBuf) + + if debug { + l.Debugf("write uncompressed message; %v (len=%d)", hm.hdr, len(uncBuf)) + } + } + } else { + if debug { + l.Debugf("write empty message; %v", hm.hdr) + } + binary.BigEndian.PutUint32(msgBuf[4:8], 0) + msgBuf = msgBuf[:8] + } + + binary.BigEndian.PutUint32(msgBuf[0:4], encodeHeader(hm.hdr)) + + if err == nil { + var n int + n, err = c.cw.Write(msgBuf) + if debug { + l.Debugf("wrote %d bytes on the wire", n) + } + } + if err != nil { + c.close(err) + return + } + case <-c.closed: + return + } + } +} + +func (c *rawConnection) close(err error) { + c.once.Do(func() { + close(c.closed) + + c.awaitingMut.Lock() + for i, ch := range c.awaiting { + if ch != nil { + close(ch) + c.awaiting[i] = nil + } + } + c.awaitingMut.Unlock() + + go c.receiver.Close(c.id, err) + }) +} + +func (c *rawConnection) idGenerator() { + nextID := 0 + for { + nextID = (nextID + 1) & 0xfff + select { + case c.nextID <- nextID: + case <-c.closed: + return + } + } +} + +// The pingSender makes sure that we've sent a message within the last +// PingSendInterval. If we already have something sent in the last +// PingSendInterval/2, we do nothing. Otherwise we send a ping message. This +// results in an effecting ping interval of somewhere between +// PingSendInterval/2 and PingSendInterval. +func (c *rawConnection) pingSender() { + ticker := time.Tick(PingSendInterval / 2) + + for { + select { + case <-ticker: + d := time.Since(c.cw.Last()) + if d < PingSendInterval/2 { + if debug { + l.Debugln(c.id, "ping skipped after wr", d) + } + continue + } + + if debug { + l.Debugln(c.id, "ping -> after", d) + } + c.ping() + + case <-c.closed: + return + } + } +} + +// The pingReciever checks that we've received a message (any message will do, +// but we expect pings in the absence of other messages) within the last +// ReceiveTimeout. If not, we close the connection with an ErrTimeout. +func (c *rawConnection) pingReceiver() { + ticker := time.Tick(ReceiveTimeout / 2) + + for { + select { + case <-ticker: + d := time.Since(c.cr.Last()) + if d > ReceiveTimeout { + if debug { + l.Debugln(c.id, "ping timeout", d) + } + c.close(ErrTimeout) + } + + if debug { + l.Debugln(c.id, "last read within", d) + } + + case <-c.closed: + return + } + } +} + +type Statistics struct { + At time.Time + InBytesTotal int64 + OutBytesTotal int64 +} + +func (c *rawConnection) Statistics() Statistics { + return Statistics{ + At: time.Now(), + InBytesTotal: c.cr.Tot(), + OutBytesTotal: c.cw.Tot(), + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/protocol_test.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/protocol_test.go new file mode 100644 index 000000000..8a4708843 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/protocol_test.go @@ -0,0 +1,316 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "reflect" + "strings" + "testing" + "testing/quick" + + "github.com/calmh/xdr" +) + +var ( + c0ID = NewDeviceID([]byte{1}) + c1ID = NewDeviceID([]byte{2}) +) + +func TestHeaderFunctions(t *testing.T) { + f := func(ver, id, typ int) bool { + ver = int(uint(ver) % 16) + id = int(uint(id) % 4096) + typ = int(uint(typ) % 256) + h0 := header{version: ver, msgID: id, msgType: typ} + h1 := decodeHeader(encodeHeader(h0)) + return h0 == h1 + } + if err := quick.Check(f, nil); err != nil { + t.Error(err) + } +} + +func TestHeaderLayout(t *testing.T) { + var e, a uint32 + + // Version are the first four bits + e = 0xf0000000 + a = encodeHeader(header{version: 0xf}) + if a != e { + t.Errorf("Header layout incorrect; %08x != %08x", a, e) + } + + // Message ID are the following 12 bits + e = 0x0fff0000 + a = encodeHeader(header{msgID: 0xfff}) + if a != e { + t.Errorf("Header layout incorrect; %08x != %08x", a, e) + } + + // Type are the last 8 bits before reserved + e = 0x0000ff00 + a = encodeHeader(header{msgType: 0xff}) + if a != e { + t.Errorf("Header layout incorrect; %08x != %08x", a, e) + } +} + +func TestPing(t *testing.T) { + ar, aw := io.Pipe() + br, bw := io.Pipe() + + c0 := NewConnection(c0ID, ar, bw, newTestModel(), "name", CompressAlways).(wireFormatConnection).next.(*rawConnection) + c0.Start() + c1 := NewConnection(c1ID, br, aw, newTestModel(), "name", CompressAlways).(wireFormatConnection).next.(*rawConnection) + c1.Start() + c0.ClusterConfig(ClusterConfigMessage{}) + c1.ClusterConfig(ClusterConfigMessage{}) + + if ok := c0.ping(); !ok { + t.Error("c0 ping failed") + } + if ok := c1.ping(); !ok { + t.Error("c1 ping failed") + } +} + +func TestVersionErr(t *testing.T) { + m0 := newTestModel() + m1 := newTestModel() + + ar, aw := io.Pipe() + br, bw := io.Pipe() + + c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).next.(*rawConnection) + c0.Start() + c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways) + c1.Start() + c0.ClusterConfig(ClusterConfigMessage{}) + c1.ClusterConfig(ClusterConfigMessage{}) + + w := xdr.NewWriter(c0.cw) + w.WriteUint32(encodeHeader(header{ + version: 2, + msgID: 0, + msgType: 0, + })) + w.WriteUint32(0) // Avoids reader closing due to EOF + + if !m1.isClosed() { + t.Error("Connection should close due to unknown version") + } +} + +func TestTypeErr(t *testing.T) { + m0 := newTestModel() + m1 := newTestModel() + + ar, aw := io.Pipe() + br, bw := io.Pipe() + + c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).next.(*rawConnection) + c0.Start() + c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways) + c1.Start() + c0.ClusterConfig(ClusterConfigMessage{}) + c1.ClusterConfig(ClusterConfigMessage{}) + + w := xdr.NewWriter(c0.cw) + w.WriteUint32(encodeHeader(header{ + version: 0, + msgID: 0, + msgType: 42, + })) + w.WriteUint32(0) // Avoids reader closing due to EOF + + if !m1.isClosed() { + t.Error("Connection should close due to unknown message type") + } +} + +func TestClose(t *testing.T) { + m0 := newTestModel() + m1 := newTestModel() + + ar, aw := io.Pipe() + br, bw := io.Pipe() + + c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).next.(*rawConnection) + c0.Start() + c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways) + c1.Start() + c0.ClusterConfig(ClusterConfigMessage{}) + c1.ClusterConfig(ClusterConfigMessage{}) + + c0.close(nil) + + <-c0.closed + if !m0.isClosed() { + t.Fatal("Connection should be closed") + } + + // None of these should panic, some should return an error + + if c0.ping() { + t.Error("Ping should not return true") + } + + c0.Index("default", nil, 0, nil) + c0.Index("default", nil, 0, nil) + + if _, err := c0.Request("default", "foo", 0, 0, nil, 0, nil); err == nil { + t.Error("Request should return an error") + } +} + +func TestElementSizeExceededNested(t *testing.T) { + m := ClusterConfigMessage{ + Folders: []Folder{ + {ID: "longstringlongstringlongstringinglongstringlongstringlonlongstringlongstringlon"}, + }, + } + _, err := m.EncodeXDR(ioutil.Discard) + if err == nil { + t.Errorf("ID length %d > max 64, but no error", len(m.Folders[0].ID)) + } +} + +func TestMarshalIndexMessage(t *testing.T) { + var quickCfg = &quick.Config{MaxCountScale: 10} + if testing.Short() { + quickCfg = nil + } + + f := func(m1 IndexMessage) bool { + for i, f := range m1.Files { + m1.Files[i].CachedSize = 0 + for j := range f.Blocks { + f.Blocks[j].Offset = 0 + if len(f.Blocks[j].Hash) == 0 { + f.Blocks[j].Hash = nil + } + } + } + + return testMarshal(t, "index", &m1, &IndexMessage{}) + } + + if err := quick.Check(f, quickCfg); err != nil { + t.Error(err) + } +} + +func TestMarshalRequestMessage(t *testing.T) { + var quickCfg = &quick.Config{MaxCountScale: 10} + if testing.Short() { + quickCfg = nil + } + + f := func(m1 RequestMessage) bool { + return testMarshal(t, "request", &m1, &RequestMessage{}) + } + + if err := quick.Check(f, quickCfg); err != nil { + t.Error(err) + } +} + +func TestMarshalResponseMessage(t *testing.T) { + var quickCfg = &quick.Config{MaxCountScale: 10} + if testing.Short() { + quickCfg = nil + } + + f := func(m1 ResponseMessage) bool { + if len(m1.Data) == 0 { + m1.Data = nil + } + return testMarshal(t, "response", &m1, &ResponseMessage{}) + } + + if err := quick.Check(f, quickCfg); err != nil { + t.Error(err) + } +} + +func TestMarshalClusterConfigMessage(t *testing.T) { + var quickCfg = &quick.Config{MaxCountScale: 10} + if testing.Short() { + quickCfg = nil + } + + f := func(m1 ClusterConfigMessage) bool { + return testMarshal(t, "clusterconfig", &m1, &ClusterConfigMessage{}) + } + + if err := quick.Check(f, quickCfg); err != nil { + t.Error(err) + } +} + +func TestMarshalCloseMessage(t *testing.T) { + var quickCfg = &quick.Config{MaxCountScale: 10} + if testing.Short() { + quickCfg = nil + } + + f := func(m1 CloseMessage) bool { + return testMarshal(t, "close", &m1, &CloseMessage{}) + } + + if err := quick.Check(f, quickCfg); err != nil { + t.Error(err) + } +} + +type message interface { + EncodeXDR(io.Writer) (int, error) + DecodeXDR(io.Reader) error +} + +func testMarshal(t *testing.T, prefix string, m1, m2 message) bool { + var buf bytes.Buffer + + failed := func(bc []byte) { + bs, _ := json.MarshalIndent(m1, "", " ") + ioutil.WriteFile(prefix+"-1.txt", bs, 0644) + bs, _ = json.MarshalIndent(m2, "", " ") + ioutil.WriteFile(prefix+"-2.txt", bs, 0644) + if len(bc) > 0 { + f, _ := os.Create(prefix + "-data.txt") + fmt.Fprint(f, hex.Dump(bc)) + f.Close() + } + } + + _, err := m1.EncodeXDR(&buf) + if err != nil && strings.Contains(err.Error(), "exceeds size") { + return true + } + if err != nil { + failed(nil) + t.Fatal(err) + } + + bc := make([]byte, len(buf.Bytes())) + copy(bc, buf.Bytes()) + + err = m2.DecodeXDR(&buf) + if err != nil { + failed(bc) + t.Fatal(err) + } + + ok := reflect.DeepEqual(m1, m2) + if !ok { + failed(bc) + } + return ok +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector.go new file mode 100644 index 000000000..edd156143 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector.go @@ -0,0 +1,115 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +// The Vector type represents a version vector. The zero value is a usable +// version vector. The vector has slice semantics and some operations on it +// are "append-like" in that they may return the same vector modified, or a +// new allocated Vector with the modified contents. +type Vector []Counter + +// Counter represents a single counter in the version vector. +type Counter struct { + ID uint64 + Value uint64 +} + +// Update returns a Vector with the index for the specific ID incremented by +// one. If it is possible, the vector v is updated and returned. If it is not, +// a copy will be created, updated and returned. +func (v Vector) Update(ID uint64) Vector { + for i := range v { + if v[i].ID == ID { + // Update an existing index + v[i].Value++ + return v + } else if v[i].ID > ID { + // Insert a new index + nv := make(Vector, len(v)+1) + copy(nv, v[:i]) + nv[i].ID = ID + nv[i].Value = 1 + copy(nv[i+1:], v[i:]) + return nv + } + } + // Append a new new index + return append(v, Counter{ID, 1}) +} + +// Merge returns the vector containing the maximum indexes from a and b. If it +// is possible, the vector a is updated and returned. If it is not, a copy +// will be created, updated and returned. +func (a Vector) Merge(b Vector) Vector { + var ai, bi int + for bi < len(b) { + if ai == len(a) { + // We've reach the end of a, all that remains are appends + return append(a, b[bi:]...) + } + + if a[ai].ID > b[bi].ID { + // The index from b should be inserted here + n := make(Vector, len(a)+1) + copy(n, a[:ai]) + n[ai] = b[bi] + copy(n[ai+1:], a[ai:]) + a = n + } + + if a[ai].ID == b[bi].ID { + if v := b[bi].Value; v > a[ai].Value { + a[ai].Value = v + } + } + + if bi < len(b) && a[ai].ID == b[bi].ID { + bi++ + } + ai++ + } + + return a +} + +// Copy returns an identical vector that is not shared with v. +func (v Vector) Copy() Vector { + nv := make(Vector, len(v)) + copy(nv, v) + return nv +} + +// Equal returns true when the two vectors are equivalent. +func (a Vector) Equal(b Vector) bool { + return a.Compare(b) == Equal +} + +// LesserEqual returns true when the two vectors are equivalent or a is Lesser +// than b. +func (a Vector) LesserEqual(b Vector) bool { + comp := a.Compare(b) + return comp == Lesser || comp == Equal +} + +// LesserEqual returns true when the two vectors are equivalent or a is Greater +// than b. +func (a Vector) GreaterEqual(b Vector) bool { + comp := a.Compare(b) + return comp == Greater || comp == Equal +} + +// Concurrent returns true when the two vectors are concrurrent. +func (a Vector) Concurrent(b Vector) bool { + comp := a.Compare(b) + return comp == ConcurrentGreater || comp == ConcurrentLesser +} + +// Counter returns the current value of the given counter ID. +func (v Vector) Counter(id uint64) uint64 { + for _, c := range v { + if c.ID == id { + return c.Value + } + } + return 0 +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_compare.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_compare.go new file mode 100644 index 000000000..9735ec9d1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_compare.go @@ -0,0 +1,89 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +// Ordering represents the relationship between two Vectors. +type Ordering int + +const ( + Equal Ordering = iota + Greater + Lesser + ConcurrentLesser + ConcurrentGreater +) + +// There's really no such thing as "concurrent lesser" and "concurrent +// greater" in version vectors, just "concurrent". But it's useful to be able +// to get a strict ordering between versions for stable sorts and so on, so we +// return both variants. The convenience method Concurrent() can be used to +// check for either case. + +// Compare returns the Ordering that describes a's relation to b. +func (a Vector) Compare(b Vector) Ordering { + var ai, bi int // index into a and b + var av, bv Counter // value at current index + + result := Equal + + for ai < len(a) || bi < len(b) { + var aMissing, bMissing bool + + if ai < len(a) { + av = a[ai] + } else { + av = Counter{} + aMissing = true + } + + if bi < len(b) { + bv = b[bi] + } else { + bv = Counter{} + bMissing = true + } + + switch { + case av.ID == bv.ID: + // We have a counter value for each side + if av.Value > bv.Value { + if result == Lesser { + return ConcurrentLesser + } + result = Greater + } else if av.Value < bv.Value { + if result == Greater { + return ConcurrentGreater + } + result = Lesser + } + + case !aMissing && av.ID < bv.ID || bMissing: + // Value is missing on the b side + if av.Value > 0 { + if result == Lesser { + return ConcurrentLesser + } + result = Greater + } + + case !bMissing && bv.ID < av.ID || aMissing: + // Value is missing on the a side + if bv.Value > 0 { + if result == Greater { + return ConcurrentGreater + } + result = Lesser + } + } + + if ai < len(a) && (av.ID <= bv.ID || bMissing) { + ai++ + } + if bi < len(b) && (bv.ID <= av.ID || aMissing) { + bi++ + } + } + + return result +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_compare_test.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_compare_test.go new file mode 100644 index 000000000..78b6abe43 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_compare_test.go @@ -0,0 +1,249 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +import ( + "math" + "testing" +) + +func TestCompare(t *testing.T) { + testcases := []struct { + a, b Vector + r Ordering + }{ + // Empty vectors are identical + {Vector{}, Vector{}, Equal}, + {Vector{}, nil, Equal}, + {nil, Vector{}, Equal}, + {nil, Vector{Counter{42, 0}}, Equal}, + {Vector{}, Vector{Counter{42, 0}}, Equal}, + {Vector{Counter{42, 0}}, nil, Equal}, + {Vector{Counter{42, 0}}, Vector{}, Equal}, + + // Zero is the implied value for a missing Counter + { + Vector{Counter{42, 0}}, + Vector{Counter{77, 0}}, + Equal, + }, + + // Equal vectors are equal + { + Vector{Counter{42, 33}}, + Vector{Counter{42, 33}}, + Equal, + }, + { + Vector{Counter{42, 33}, Counter{77, 24}}, + Vector{Counter{42, 33}, Counter{77, 24}}, + Equal, + }, + + // These a-vectors are all greater than the b-vector + { + Vector{Counter{42, 1}}, + nil, + Greater, + }, + { + Vector{Counter{42, 1}}, + Vector{}, + Greater, + }, + { + Vector{Counter{0, 1}}, + Vector{Counter{0, 0}}, + Greater, + }, + { + Vector{Counter{42, 1}}, + Vector{Counter{42, 0}}, + Greater, + }, + { + Vector{Counter{math.MaxUint64, 1}}, + Vector{Counter{math.MaxUint64, 0}}, + Greater, + }, + { + Vector{Counter{0, math.MaxUint64}}, + Vector{Counter{0, 0}}, + Greater, + }, + { + Vector{Counter{42, math.MaxUint64}}, + Vector{Counter{42, 0}}, + Greater, + }, + { + Vector{Counter{math.MaxUint64, math.MaxUint64}}, + Vector{Counter{math.MaxUint64, 0}}, + Greater, + }, + { + Vector{Counter{0, math.MaxUint64}}, + Vector{Counter{0, math.MaxUint64 - 1}}, + Greater, + }, + { + Vector{Counter{42, math.MaxUint64}}, + Vector{Counter{42, math.MaxUint64 - 1}}, + Greater, + }, + { + Vector{Counter{math.MaxUint64, math.MaxUint64}}, + Vector{Counter{math.MaxUint64, math.MaxUint64 - 1}}, + Greater, + }, + { + Vector{Counter{42, 2}}, + Vector{Counter{42, 1}}, + Greater, + }, + { + Vector{Counter{22, 22}, Counter{42, 2}}, + Vector{Counter{22, 22}, Counter{42, 1}}, + Greater, + }, + { + Vector{Counter{42, 2}, Counter{77, 3}}, + Vector{Counter{42, 1}, Counter{77, 3}}, + Greater, + }, + { + Vector{Counter{22, 22}, Counter{42, 2}, Counter{77, 3}}, + Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}}, + Greater, + }, + { + Vector{Counter{22, 23}, Counter{42, 2}, Counter{77, 4}}, + Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}}, + Greater, + }, + + // These a-vectors are all lesser than the b-vector + {nil, Vector{Counter{42, 1}}, Lesser}, + {Vector{}, Vector{Counter{42, 1}}, Lesser}, + { + Vector{Counter{42, 0}}, + Vector{Counter{42, 1}}, + Lesser, + }, + { + Vector{Counter{42, 1}}, + Vector{Counter{42, 2}}, + Lesser, + }, + { + Vector{Counter{22, 22}, Counter{42, 1}}, + Vector{Counter{22, 22}, Counter{42, 2}}, + Lesser, + }, + { + Vector{Counter{42, 1}, Counter{77, 3}}, + Vector{Counter{42, 2}, Counter{77, 3}}, + Lesser, + }, + { + Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}}, + Vector{Counter{22, 22}, Counter{42, 2}, Counter{77, 3}}, + Lesser, + }, + { + Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}}, + Vector{Counter{22, 23}, Counter{42, 2}, Counter{77, 4}}, + Lesser, + }, + + // These are all in conflict + { + Vector{Counter{42, 2}}, + Vector{Counter{43, 1}}, + ConcurrentGreater, + }, + { + Vector{Counter{43, 1}}, + Vector{Counter{42, 2}}, + ConcurrentLesser, + }, + { + Vector{Counter{22, 23}, Counter{42, 1}}, + Vector{Counter{22, 22}, Counter{42, 2}}, + ConcurrentGreater, + }, + { + Vector{Counter{22, 21}, Counter{42, 2}}, + Vector{Counter{22, 22}, Counter{42, 1}}, + ConcurrentLesser, + }, + { + Vector{Counter{22, 21}, Counter{42, 2}, Counter{43, 1}}, + Vector{Counter{20, 1}, Counter{22, 22}, Counter{42, 1}}, + ConcurrentLesser, + }, + } + + for i, tc := range testcases { + // Test real Compare + if r := tc.a.Compare(tc.b); r != tc.r { + t.Errorf("%d: %+v.Compare(%+v) == %v (expected %v)", i, tc.a, tc.b, r, tc.r) + } + + // Test convenience functions + switch tc.r { + case Greater: + if tc.a.Equal(tc.b) { + t.Errorf("%+v == %+v", tc.a, tc.b) + } + if tc.a.Concurrent(tc.b) { + t.Errorf("%+v concurrent %+v", tc.a, tc.b) + } + if !tc.a.GreaterEqual(tc.b) { + t.Errorf("%+v not >= %+v", tc.a, tc.b) + } + if tc.a.LesserEqual(tc.b) { + t.Errorf("%+v <= %+v", tc.a, tc.b) + } + case Lesser: + if tc.a.Concurrent(tc.b) { + t.Errorf("%+v concurrent %+v", tc.a, tc.b) + } + if tc.a.Equal(tc.b) { + t.Errorf("%+v == %+v", tc.a, tc.b) + } + if tc.a.GreaterEqual(tc.b) { + t.Errorf("%+v >= %+v", tc.a, tc.b) + } + if !tc.a.LesserEqual(tc.b) { + t.Errorf("%+v not <= %+v", tc.a, tc.b) + } + case Equal: + if tc.a.Concurrent(tc.b) { + t.Errorf("%+v concurrent %+v", tc.a, tc.b) + } + if !tc.a.Equal(tc.b) { + t.Errorf("%+v not == %+v", tc.a, tc.b) + } + if !tc.a.GreaterEqual(tc.b) { + t.Errorf("%+v not <= %+v", tc.a, tc.b) + } + if !tc.a.LesserEqual(tc.b) { + t.Errorf("%+v not <= %+v", tc.a, tc.b) + } + case ConcurrentLesser, ConcurrentGreater: + if !tc.a.Concurrent(tc.b) { + t.Errorf("%+v not concurrent %+v", tc.a, tc.b) + } + if tc.a.Equal(tc.b) { + t.Errorf("%+v == %+v", tc.a, tc.b) + } + if tc.a.GreaterEqual(tc.b) { + t.Errorf("%+v >= %+v", tc.a, tc.b) + } + if tc.a.LesserEqual(tc.b) { + t.Errorf("%+v <= %+v", tc.a, tc.b) + } + } + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_test.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_test.go new file mode 100644 index 000000000..c01255e7a --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_test.go @@ -0,0 +1,134 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +import "testing" + +func TestUpdate(t *testing.T) { + var v Vector + + // Append + + v = v.Update(42) + expected := Vector{Counter{42, 1}} + + if v.Compare(expected) != Equal { + t.Errorf("Update error, %+v != %+v", v, expected) + } + + // Insert at front + + v = v.Update(36) + expected = Vector{Counter{36, 1}, Counter{42, 1}} + + if v.Compare(expected) != Equal { + t.Errorf("Update error, %+v != %+v", v, expected) + } + + // Insert in moddle + + v = v.Update(37) + expected = Vector{Counter{36, 1}, Counter{37, 1}, Counter{42, 1}} + + if v.Compare(expected) != Equal { + t.Errorf("Update error, %+v != %+v", v, expected) + } + + // Update existing + + v = v.Update(37) + expected = Vector{Counter{36, 1}, Counter{37, 2}, Counter{42, 1}} + + if v.Compare(expected) != Equal { + t.Errorf("Update error, %+v != %+v", v, expected) + } +} + +func TestCopy(t *testing.T) { + v0 := Vector{Counter{42, 1}} + v1 := v0.Copy() + v1.Update(42) + if v0.Compare(v1) != Lesser { + t.Errorf("Copy error, %+v should be ancestor of %+v", v0, v1) + } +} + +func TestMerge(t *testing.T) { + testcases := []struct { + a, b, m Vector + }{ + // No-ops + { + Vector{}, + Vector{}, + Vector{}, + }, + { + Vector{Counter{22, 1}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + }, + + // Appends + { + Vector{}, + Vector{Counter{22, 1}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + }, + { + Vector{Counter{22, 1}}, + Vector{Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + }, + { + Vector{Counter{22, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + }, + + // Insert + { + Vector{Counter{22, 1}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{23, 2}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{23, 2}, Counter{42, 1}}, + }, + { + Vector{Counter{42, 1}}, + Vector{Counter{22, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + }, + + // Update + { + Vector{Counter{22, 1}, Counter{42, 2}}, + Vector{Counter{22, 2}, Counter{42, 1}}, + Vector{Counter{22, 2}, Counter{42, 2}}, + }, + + // All of the above + { + Vector{Counter{10, 1}, Counter{20, 2}, Counter{30, 1}}, + Vector{Counter{5, 1}, Counter{10, 2}, Counter{15, 1}, Counter{20, 1}, Counter{25, 1}, Counter{35, 1}}, + Vector{Counter{5, 1}, Counter{10, 2}, Counter{15, 1}, Counter{20, 2}, Counter{25, 1}, Counter{30, 1}, Counter{35, 1}}, + }, + } + + for i, tc := range testcases { + if m := tc.a.Merge(tc.b); m.Compare(tc.m) != Equal { + t.Errorf("%d: %+v.Merge(%+v) == %+v (expected %+v)", i, tc.a, tc.b, m, tc.m) + } + } +} + +func TestCounterValue(t *testing.T) { + v0 := Vector{Counter{42, 1}, Counter{64, 5}} + if v0.Counter(42) != 1 { + t.Error("Counter error, %d != %d", v0.Counter(42), 1) + } + if v0.Counter(64) != 5 { + t.Error("Counter error, %d != %d", v0.Counter(64), 5) + } + if v0.Counter(72) != 0 { + t.Error("Counter error, %d != %d", v0.Counter(72), 0) + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_xdr.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_xdr.go new file mode 100644 index 000000000..01efa7e4e --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/vector_xdr.go @@ -0,0 +1,43 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +import "github.com/calmh/xdr" + +// This stuff is hacked up manually because genxdr doesn't support 'type +// Vector []Counter' declarations and it was tricky when I tried to add it... + +type xdrWriter interface { + WriteUint32(uint32) (int, error) + WriteUint64(uint64) (int, error) +} +type xdrReader interface { + ReadUint32() uint32 + ReadUint64() uint64 +} + +// EncodeXDRInto encodes the vector as an XDR object into the given XDR +// encoder. +func (v Vector) EncodeXDRInto(w xdrWriter) (int, error) { + w.WriteUint32(uint32(len(v))) + for i := range v { + w.WriteUint64(v[i].ID) + w.WriteUint64(v[i].Value) + } + return 4 + 16*len(v), nil +} + +// DecodeXDRFrom decodes the XDR objects from the given reader into itself. +func (v *Vector) DecodeXDRFrom(r xdrReader) error { + l := int(r.ReadUint32()) + if l > 1e6 { + return xdr.ElementSizeExceeded("number of counters", l, 1e6) + } + n := make(Vector, l) + for i := range n { + n[i].ID = r.ReadUint64() + n[i].Value = r.ReadUint64() + } + *v = n + return nil +} diff --git a/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/wireformat.go b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/wireformat.go new file mode 100644 index 000000000..66b02ed6f --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/syncthing/lib/protocol/wireformat.go @@ -0,0 +1,60 @@ +// Copyright (C) 2014 The Protocol Authors. + +package protocol + +import ( + "path/filepath" + + "golang.org/x/text/unicode/norm" +) + +type wireFormatConnection struct { + next Connection +} + +func (c wireFormatConnection) Start() { + c.next.Start() +} + +func (c wireFormatConnection) ID() DeviceID { + return c.next.ID() +} + +func (c wireFormatConnection) Name() string { + return c.next.Name() +} + +func (c wireFormatConnection) Index(folder string, fs []FileInfo, flags uint32, options []Option) error { + var myFs = make([]FileInfo, len(fs)) + copy(myFs, fs) + + for i := range fs { + myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name)) + } + + return c.next.Index(folder, myFs, flags, options) +} + +func (c wireFormatConnection) IndexUpdate(folder string, fs []FileInfo, flags uint32, options []Option) error { + var myFs = make([]FileInfo, len(fs)) + copy(myFs, fs) + + for i := range fs { + myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name)) + } + + return c.next.IndexUpdate(folder, myFs, flags, options) +} + +func (c wireFormatConnection) Request(folder, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) { + name = norm.NFC.String(filepath.ToSlash(name)) + return c.next.Request(folder, name, offset, size, hash, flags, options) +} + +func (c wireFormatConnection) ClusterConfig(config ClusterConfigMessage) { + c.next.ClusterConfig(config) +} + +func (c wireFormatConnection) Statistics() Statistics { + return c.next.Statistics() +} diff --git a/Godeps/_workspace/src/github.com/thejerf/suture/.travis.yml b/Godeps/_workspace/src/github.com/thejerf/suture/.travis.yml new file mode 100644 index 000000000..814710d4e --- /dev/null +++ b/Godeps/_workspace/src/github.com/thejerf/suture/.travis.yml @@ -0,0 +1,7 @@ +language: go +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - tip diff --git a/Godeps/_workspace/src/github.com/thejerf/suture/LICENSE b/Godeps/_workspace/src/github.com/thejerf/suture/LICENSE new file mode 100644 index 000000000..018bc1c4d --- /dev/null +++ b/Godeps/_workspace/src/github.com/thejerf/suture/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Barracuda Networks, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/thejerf/suture/README.md b/Godeps/_workspace/src/github.com/thejerf/suture/README.md new file mode 100644 index 000000000..20362d7d0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/thejerf/suture/README.md @@ -0,0 +1,50 @@ +Suture +====== + +[![Build Status](https://travis-ci.org/thejerf/suture.png?branch=master)](https://travis-ci.org/thejerf/suture) + +Suture provides Erlang-ish supervisor trees for Go. "Supervisor trees" -> +"sutree" -> "suture" -> holds your code together when it's trying to die. + +This library has hit maturity, and isn't expected to be changed +radically. This can also be imported via gopkg.in/thejerf/suture.v1 . + +It is intended to deal gracefully with the real failure cases that can +occur with supervision trees (such as burning all your CPU time endlessly +restarting dead services), while also making no unnecessary demands on the +"service" code, and providing hooks to perform adequate logging with in a +production environment. + +[A blog post describing the design decisions](http://www.jerf.org/iri/post/2930) +is available. + +This module is fully covered with [godoc](http://godoc.org/github.com/thejerf/suture), +including an example, usage, and everything else you might expect from a +README.md on GitHub. (DRY.) + +Code Signing +------------ + +Starting with the commit after ac7cf8591b, I will be signing this repository +with the ["jerf" keybase account](https://keybase.io/jerf). + +Aspiration +---------- + +One of the big wins the Erlang community has with their pervasive OTP +support is that it makes it easy for them to distribute libraries that +easily fit into the OTP paradigm. It ought to someday be considered a good +idea to distribute libraries that provide some sort of supervisor tree +functionality out of the box. It is possible to provide this functionality +without explicitly depending on the Suture library. + +Changelog +--------- + +suture uses semantic versioning. + +1. 1.0.0 + * Initial release. +2. 1.0.1 + * Fixed data race on the .state variable. + diff --git a/Godeps/_workspace/src/github.com/thejerf/suture/pre-commit b/Godeps/_workspace/src/github.com/thejerf/suture/pre-commit new file mode 100644 index 000000000..6199d610f --- /dev/null +++ b/Godeps/_workspace/src/github.com/thejerf/suture/pre-commit @@ -0,0 +1,12 @@ +#!/bin/bash + +GOLINTOUT=$(golint *go) + +if [ ! -z "$GOLINTOUT" -o "$?" != 0 ]; then + echo golint failed: + echo $GOLINTOUT + exit 1 +fi + +go test + diff --git a/Godeps/_workspace/src/github.com/thejerf/suture/suture.go b/Godeps/_workspace/src/github.com/thejerf/suture/suture.go new file mode 100644 index 000000000..1cd0a731f --- /dev/null +++ b/Godeps/_workspace/src/github.com/thejerf/suture/suture.go @@ -0,0 +1,690 @@ +/* + +Package suture provides Erlang-like supervisor trees. + +This implements Erlang-esque supervisor trees, as adapted for Go. This is +intended to be an industrial-strength implementation, but it has not yet +been deployed in a hostile environment. (It's headed there, though.) + +Supervisor Tree -> SuTree -> suture -> holds your code together when it's +trying to fall apart. + +Why use Suture? + + * You want to write bullet-resistant services that will remain available + despite unforeseen failure. + * You need the code to be smart enough not to consume 100% of the CPU + restarting things. + * You want to easily compose multiple such services in one program. + * You want the Erlang programmers to stop lording their supervision + trees over you. + +Suture has 100% test coverage, and is golint clean. This doesn't prove it +free of bugs, but it shows I care. + +A blog post describing the design decisions is available at +http://www.jerf.org/iri/post/2930 . + +Using Suture + +To idiomatically use Suture, create a Supervisor which is your top level +"application" supervisor. This will often occur in your program's "main" +function. + +Create "Service"s, which implement the Service interface. .Add() them +to your Supervisor. Supervisors are also services, so you can create a +tree structure here, depending on the exact combination of restarts +you want to create. + +As a special case, when adding Supervisors to Supervisors, the "sub" +supervisor will have the "super" supervisor's Log function copied. +This allows you to set one log function on the "top" supervisor, and +have it propagate down to all the sub-supervisors. This also allows +libraries or modules to provide Supervisors without having to commit +their users to a particular logging method. + +Finally, as what is probably the last line of your main() function, call +.Serve() on your top level supervisor. This will start all the services +you've defined. + +See the Example for an example, using a simple service that serves out +incrementing integers. + +*/ +package suture + +import ( + "errors" + "fmt" + "log" + "math" + "runtime" + "sync" + "sync/atomic" + "time" +) + +const ( + notRunning = iota + normal + paused +) + +type supervisorID uint32 +type serviceID uint32 + +var currentSupervisorID uint32 + +// ErrWrongSupervisor is returned by the (*Supervisor).Remove method +// if you pass a ServiceToken from the wrong Supervisor. +var ErrWrongSupervisor = errors.New("wrong supervisor for this service token, no service removed") + +// ServiceToken is an opaque identifier that can be used to terminate a service that +// has been Add()ed to a Supervisor. +type ServiceToken struct { + id uint64 +} + +/* +Supervisor is the core type of the module that represents a Supervisor. + +Supervisors should be constructed either by New or NewSimple. + +Once constructed, a Supervisor should be started in one of three ways: + + 1. Calling .Serve(). + 2. Calling .ServeBackground(). + 3. Adding it to an existing Supervisor. + +Calling Serve will cause the supervisor to run until it is shut down by +an external user calling Stop() on it. If that never happens, it simply +runs forever. I suggest creating your services in Supervisors, then making +a Serve() call on your top-level Supervisor be the last line of your main +func. + +Calling ServeBackground will CORRECTLY start the supervisor running in a +new goroutine. You do not want to just: + + go supervisor.Serve() + +because that will briefly create a race condition as it starts up, if you +try to .Add() services immediately afterward. + +*/ +type Supervisor struct { + Name string + id supervisorID + + failureDecay float64 + failureThreshold float64 + failureBackoff time.Duration + timeout time.Duration + log func(string) + services map[serviceID]Service + lastFail time.Time + failures float64 + restartQueue []serviceID + serviceCounter serviceID + control chan supervisorMessage + resumeTimer <-chan time.Time + + // The testing uses the ability to grab these individual logging functions + // and get inside of suture's handling at a deep level. + // If you ever come up with some need to get into these, submit a pull + // request to make them public and some smidge of justification, and + // I'll happily do it. + // But since I've now changed the signature on these once, I'm glad I + // didn't start with them public... :) + logBadStop func(*Supervisor, Service) + logFailure func(supervisor *Supervisor, service Service, currentFailures float64, failureThreshold float64, restarting bool, error interface{}, stacktrace []byte) + logBackoff func(*Supervisor, bool) + + // avoid a dependency on github.com/thejerf/abtime by just implementing + // a minimal chunk. + getNow func() time.Time + getResume func(time.Duration) <-chan time.Time + + sync.Mutex + state uint8 +} + +// Spec is used to pass arguments to the New function to create a +// supervisor. See the New function for full documentation. +type Spec struct { + Log func(string) + FailureDecay float64 + FailureThreshold float64 + FailureBackoff time.Duration + Timeout time.Duration +} + +/* + +New is the full constructor function for a supervisor. + +The name is a friendly human name for the supervisor, used in logging. Suture +does not care if this is unique, but it is good for your sanity if it is. + +If not set, the following values are used: + + * Log: A function is created that uses log.Print. + * FailureDecay: 30 seconds + * FailureThreshold: 5 failures + * FailureBackoff: 15 seconds + * Timeout: 10 seconds + +The Log function will be called when errors occur. Suture will log the +following: + + * When a service has failed, with a descriptive message about the + current backoff status, and whether it was immediately restarted + * When the supervisor has gone into its backoff mode, and when it + exits it + * When a service fails to stop + +The failureRate, failureThreshold, and failureBackoff controls how failures +are handled, in order to avoid the supervisor failure case where the +program does nothing but restarting failed services. If you do not +care how failures behave, the default values should be fine for the +vast majority of services, but if you want the details: + +The supervisor tracks the number of failures that have occurred, with an +exponential decay on the count. Every FailureDecay seconds, the number of +failures that have occurred is cut in half. (This is done smoothly with an +exponential function.) When a failure occurs, the number of failures +is incremented by one. When the number of failures passes the +FailureThreshold, the entire service waits for FailureBackoff seconds +before attempting any further restarts, at which point it resets its +failure count to zero. + +Timeout is how long Suture will wait for a service to properly terminate. + +*/ +func New(name string, spec Spec) (s *Supervisor) { + s = new(Supervisor) + + s.Name = name + s.id = supervisorID(atomic.AddUint32(¤tSupervisorID, 1)) + + if spec.Log == nil { + s.log = func(msg string) { + log.Print(fmt.Sprintf("Supervisor %s: %s", s.Name, msg)) + } + } else { + s.log = spec.Log + } + + if spec.FailureDecay == 0 { + s.failureDecay = 30 + } else { + s.failureDecay = spec.FailureDecay + } + if spec.FailureThreshold == 0 { + s.failureThreshold = 5 + } else { + s.failureThreshold = spec.FailureThreshold + } + if spec.FailureBackoff == 0 { + s.failureBackoff = time.Second * 15 + } else { + s.failureBackoff = spec.FailureBackoff + } + if spec.Timeout == 0 { + s.timeout = time.Second * 10 + } else { + s.timeout = spec.Timeout + } + + // overriding these allows for testing the threshold behavior + s.getNow = time.Now + s.getResume = time.After + + s.control = make(chan supervisorMessage) + s.services = make(map[serviceID]Service) + s.restartQueue = make([]serviceID, 0, 1) + s.resumeTimer = make(chan time.Time) + + // set up the default logging handlers + s.logBadStop = func(supervisor *Supervisor, service Service) { + s.log(fmt.Sprintf("%s: Service %s failed to terminate in a timely manner", serviceName(supervisor), serviceName(service))) + } + s.logFailure = func(supervisor *Supervisor, service Service, failures float64, threshold float64, restarting bool, err interface{}, st []byte) { + var errString string + + e, canError := err.(error) + if canError { + errString = e.Error() + } else { + errString = fmt.Sprintf("%#v", err) + } + + s.log(fmt.Sprintf("%s: Failed service '%s' (%f failures of %f), restarting: %#v, error: %s, stacktrace: %s", serviceName(supervisor), serviceName(service), failures, threshold, restarting, errString, string(st))) + } + s.logBackoff = func(s *Supervisor, entering bool) { + if entering { + s.log("Entering the backoff state.") + } else { + s.log("Exiting backoff state.") + } + } + + return +} + +func serviceName(service Service) (serviceName string) { + stringer, canStringer := service.(fmt.Stringer) + if canStringer { + serviceName = stringer.String() + } else { + serviceName = fmt.Sprintf("%#v", service) + } + return +} + +// NewSimple is a convenience function to create a service with just a name +// and the sensible defaults. +func NewSimple(name string) *Supervisor { + return New(name, Spec{}) +} + +/* +Service is the interface that describes a service to a Supervisor. + +Serve Method + +The Serve method is called by a Supervisor to start the service. +The service should execute within the goroutine that this is +called in. If this function either returns or panics, the Supervisor +will call it again. + +A Serve method SHOULD do as much cleanup of the state as possible, +to prevent any corruption in the previous state from crashing the +service again. + +Stop Method + +This method is used by the supervisor to stop the service. Calling this +directly on a Service given to a Supervisor will simply result in the +Service being restarted; use the Supervisor's .Remove(ServiceToken) method +to stop a service. A supervisor will call .Stop() only once. Thus, it may +be as destructive as it likes to get the service to stop. + +Once Stop has been called on a Service, the Service SHOULD NOT be +reused in any other supervisor! Because of the impossibility of +guaranteeing that the service has actually stopped in Go, you can't +prove that you won't be starting two goroutines using the exact +same memory to store state, causing completely unpredictable behavior. + +Stop should not return until the service has actually stopped. +"Stopped" here is defined as "the service will stop servicing any +further requests in the future". For instance, a common implementation +is to receive a message on a dedicated "stop" channel and immediately +returning. Once the stop command has been processed, the service is +stopped. + +Another common Stop implementation is to forcibly close an open socket +or other resource, which will cause detectable errors to manifest in the +service code. Bear in mind that to perfectly correctly use this +approach requires a bit more work to handle the chance of a Stop +command coming in before the resource has been created. + +If a service does not Stop within the supervisor's timeout duration, a log +entry will be made with a descriptive string to that effect. This does +not guarantee that the service is hung; it may still get around to being +properly stopped in the future. Until the service is fully stopped, +both the service and the spawned goroutine trying to stop it will be +"leaked". + +Stringer Interface + +It is not mandatory to implement the fmt.Stringer interface on your +service, but if your Service does happen to implement that, the log +messages that describe your service will use that when naming the +service. Otherwise, you'll see the GoString of your service object, +obtained via fmt.Sprintf("%#v", service). + +*/ +type Service interface { + Serve() + Stop() +} + +/* +Add adds a service to this supervisor. + +If the supervisor is currently running, the service will be started +immediately. If the supervisor is not currently running, the service +will be started when the supervisor is. + +The returned ServiceID may be passed to the Remove method of the Supervisor +to terminate the service. + +As a special behavior, if the service added is itself a supervisor, the +supervisor being added will copy the Log function from the Supervisor it +is being added to. This allows factoring out providing a Supervisor +from its logging. + +*/ +func (s *Supervisor) Add(service Service) ServiceToken { + if s == nil { + panic("can't add service to nil *suture.Supervisor") + } + + if supervisor, isSupervisor := service.(*Supervisor); isSupervisor { + supervisor.logBadStop = s.logBadStop + supervisor.logFailure = s.logFailure + supervisor.logBackoff = s.logBackoff + } + + s.Lock() + if s.state == notRunning { + id := s.serviceCounter + s.serviceCounter++ + + s.services[id] = service + s.restartQueue = append(s.restartQueue, id) + + s.Unlock() + return ServiceToken{uint64(s.id)<<32 | uint64(id)} + } + s.Unlock() + + response := make(chan serviceID) + s.control <- addService{service, response} + return ServiceToken{uint64(s.id)<<32 | uint64(<-response)} +} + +// ServeBackground starts running a supervisor in its own goroutine. This +// method does not return until it is safe to use .Add() on the Supervisor. +func (s *Supervisor) ServeBackground() { + go s.Serve() + s.sync() +} + +/* +Serve starts the supervisor. You should call this on the top-level supervisor, +but nothing else. +*/ +func (s *Supervisor) Serve() { + if s == nil { + panic("Can't serve with a nil *suture.Supervisor") + } + if s.id == 0 { + panic("Can't call Serve on an incorrectly-constructed *suture.Supervisor") + } + + defer func() { + s.Lock() + s.state = notRunning + s.Unlock() + }() + + s.Lock() + if s.state != notRunning { + s.Unlock() + panic("Running a supervisor while it is already running?") + } + + s.state = normal + s.Unlock() + + // for all the services I currently know about, start them + for _, id := range s.restartQueue { + service, present := s.services[id] + if present { + s.runService(service, id) + } + } + s.restartQueue = make([]serviceID, 0, 1) + + for { + select { + case m := <-s.control: + switch msg := m.(type) { + case serviceFailed: + s.handleFailedService(msg.id, msg.err, msg.stacktrace) + case serviceEnded: + service, monitored := s.services[msg.id] + if monitored { + s.handleFailedService(msg.id, fmt.Sprintf("%s returned unexpectedly", service), []byte("[unknown stack trace]")) + } + case addService: + id := s.serviceCounter + s.serviceCounter++ + + s.services[id] = msg.service + s.runService(msg.service, id) + + msg.response <- id + case removeService: + s.removeService(msg.id) + case stopSupervisor: + for id := range s.services { + s.removeService(id) + } + return + case listServices: + services := []Service{} + for _, service := range s.services { + services = append(services, service) + } + msg.c <- services + case syncSupervisor: + // this does nothing on purpose; its sole purpose is to + // introduce a sync point via the channel receive + case panicSupervisor: + // used only by tests + panic("Panicking as requested!") + } + case _ = <-s.resumeTimer: + // We're resuming normal operation after a pause due to + // excessive thrashing + // FIXME: Ought to permit some spacing of these functions, rather + // than simply hammering through them + s.Lock() + s.state = normal + s.Unlock() + s.failures = 0 + s.logBackoff(s, false) + for _, id := range s.restartQueue { + service, present := s.services[id] + if present { + s.runService(service, id) + } + } + s.restartQueue = make([]serviceID, 0, 1) + } + } +} + +func (s *Supervisor) handleFailedService(id serviceID, err interface{}, stacktrace []byte) { + now := s.getNow() + + if s.lastFail.IsZero() { + s.lastFail = now + s.failures = 1.0 + } else { + sinceLastFail := now.Sub(s.lastFail).Seconds() + intervals := sinceLastFail / s.failureDecay + s.failures = s.failures*math.Pow(.5, intervals) + 1 + } + + if s.failures > s.failureThreshold { + s.Lock() + s.state = paused + s.Unlock() + s.logBackoff(s, true) + s.resumeTimer = s.getResume(s.failureBackoff) + } + + s.lastFail = now + + failedService, monitored := s.services[id] + + // It is possible for a service to be no longer monitored + // by the time we get here. In that case, just ignore it. + if monitored { + // this may look dangerous because the state could change, but this + // code is only ever run in the one goroutine that is permitted to + // change the state, so nothing else will. + s.Lock() + curState := s.state + s.Unlock() + if curState == normal { + s.runService(failedService, id) + s.logFailure(s, failedService, s.failures, s.failureThreshold, true, err, stacktrace) + } else { + // FIXME: When restarting, check that the service still + // exists (it may have been stopped in the meantime) + s.restartQueue = append(s.restartQueue, id) + s.logFailure(s, failedService, s.failures, s.failureThreshold, false, err, stacktrace) + } + } +} + +func (s *Supervisor) runService(service Service, id serviceID) { + go func() { + defer func() { + if r := recover(); r != nil { + buf := make([]byte, 65535, 65535) + written := runtime.Stack(buf, false) + buf = buf[:written] + s.fail(id, r, buf) + } + }() + + service.Serve() + + s.serviceEnded(id) + }() +} + +func (s *Supervisor) removeService(id serviceID) { + service, present := s.services[id] + if present { + delete(s.services, id) + go func() { + successChan := make(chan bool) + go func() { + service.Stop() + successChan <- true + }() + + failChan := s.getResume(s.timeout) + + select { + case <-successChan: + // Life is good! + case <-failChan: + s.logBadStop(s, service) + } + }() + } +} + +// String implements the fmt.Stringer interface. +func (s *Supervisor) String() string { + return s.Name +} + +// sum type pattern for type-safe message passing; see +// http://www.jerf.org/iri/post/2917 + +type supervisorMessage interface { + isSupervisorMessage() +} + +/* +Remove will remove the given service from the Supervisor, and attempt to Stop() it. +The ServiceID token comes from the Add() call. +*/ +func (s *Supervisor) Remove(id ServiceToken) error { + sID := supervisorID(id.id >> 32) + if sID != s.id { + return ErrWrongSupervisor + } + s.control <- removeService{serviceID(id.id & 0xffffffff)} + return nil +} + +/* + +Services returns a []Service containing a snapshot of the services this +Supervisor is managing. + +*/ +func (s *Supervisor) Services() []Service { + ls := listServices{make(chan []Service)} + s.control <- ls + return <-ls.c +} + +type listServices struct { + c chan []Service +} + +func (ls listServices) isSupervisorMessage() {} + +type removeService struct { + id serviceID +} + +func (rs removeService) isSupervisorMessage() {} + +func (s *Supervisor) sync() { + s.control <- syncSupervisor{} +} + +type syncSupervisor struct { +} + +func (ss syncSupervisor) isSupervisorMessage() {} + +func (s *Supervisor) fail(id serviceID, err interface{}, stacktrace []byte) { + s.control <- serviceFailed{id, err, stacktrace} +} + +type serviceFailed struct { + id serviceID + err interface{} + stacktrace []byte +} + +func (sf serviceFailed) isSupervisorMessage() {} + +func (s *Supervisor) serviceEnded(id serviceID) { + s.control <- serviceEnded{id} +} + +type serviceEnded struct { + id serviceID +} + +func (s serviceEnded) isSupervisorMessage() {} + +// added by the Add() method +type addService struct { + service Service + response chan serviceID +} + +func (as addService) isSupervisorMessage() {} + +// Stop stops the Supervisor. +func (s *Supervisor) Stop() { + s.control <- stopSupervisor{} +} + +type stopSupervisor struct { +} + +func (ss stopSupervisor) isSupervisorMessage() {} + +func (s *Supervisor) panic() { + s.control <- panicSupervisor{} +} + +type panicSupervisor struct { +} + +func (ps panicSupervisor) isSupervisorMessage() {} diff --git a/Godeps/_workspace/src/github.com/thejerf/suture/suture_simple_test.go b/Godeps/_workspace/src/github.com/thejerf/suture/suture_simple_test.go new file mode 100644 index 000000000..9afda1169 --- /dev/null +++ b/Godeps/_workspace/src/github.com/thejerf/suture/suture_simple_test.go @@ -0,0 +1,49 @@ +package suture + +import "fmt" + +type Incrementor struct { + current int + next chan int + stop chan bool +} + +func (i *Incrementor) Stop() { + fmt.Println("Stopping the service") + i.stop <- true +} + +func (i *Incrementor) Serve() { + for { + select { + case i.next <- i.current: + i.current++ + case <-i.stop: + // We sync here just to guarantee the output of "Stopping the service", + // so this passes the test reliably. + // Most services would simply "return" here. + i.stop <- true + return + } + } +} + +func ExampleNew_simple() { + supervisor := NewSimple("Supervisor") + service := &Incrementor{0, make(chan int), make(chan bool)} + supervisor.Add(service) + + go supervisor.ServeBackground() + + fmt.Println("Got:", <-service.next) + fmt.Println("Got:", <-service.next) + supervisor.Stop() + + // We sync here just to guarantee the output of "Stopping the service" + <-service.stop + + // Output: + // Got: 0 + // Got: 1 + // Stopping the service +} diff --git a/Godeps/_workspace/src/github.com/thejerf/suture/suture_test.go b/Godeps/_workspace/src/github.com/thejerf/suture/suture_test.go new file mode 100644 index 000000000..8c3547089 --- /dev/null +++ b/Godeps/_workspace/src/github.com/thejerf/suture/suture_test.go @@ -0,0 +1,616 @@ +package suture + +import ( + "errors" + "fmt" + "reflect" + "sync" + "testing" + "time" +) + +const ( + Happy = iota + Fail + Panic + Hang + UseStopChan +) + +var everMultistarted = false + +// Test that supervisors work perfectly when everything is hunky dory. +func TestTheHappyCase(t *testing.T) { + t.Parallel() + + s := NewSimple("A") + if s.String() != "A" { + t.Fatal("Can't get name from a supervisor") + } + service := NewService("B") + + s.Add(service) + + go s.Serve() + + <-service.started + + // If we stop the service, it just gets restarted + service.Stop() + <-service.started + + // And it is shut down when we stop the supervisor + service.take <- UseStopChan + s.Stop() + <-service.stop +} + +// Test that adding to a running supervisor does indeed start the service. +func TestAddingToRunningSupervisor(t *testing.T) { + t.Parallel() + + s := NewSimple("A1") + + s.ServeBackground() + defer s.Stop() + + service := NewService("B1") + s.Add(service) + + <-service.started + + services := s.Services() + if !reflect.DeepEqual([]Service{service}, services) { + t.Fatal("Can't get list of services as expected.") + } +} + +// Test what happens when services fail. +func TestFailures(t *testing.T) { + t.Parallel() + + s := NewSimple("A2") + s.failureThreshold = 3.5 + + go s.Serve() + defer func() { + // to avoid deadlocks during shutdown, we have to not try to send + // things out on channels while we're shutting down (this undoes the + // logFailure overide about 25 lines down) + s.logFailure = func(*Supervisor, Service, float64, float64, bool, interface{}, []byte) {} + s.Stop() + }() + s.sync() + + service1 := NewService("B2") + service2 := NewService("C2") + + s.Add(service1) + <-service1.started + s.Add(service2) + <-service2.started + + nowFeeder := NewNowFeeder() + pastVal := time.Unix(1000000, 0) + nowFeeder.appendTimes(pastVal) + s.getNow = nowFeeder.getter + + resumeChan := make(chan time.Time) + s.getResume = func(d time.Duration) <-chan time.Time { + return resumeChan + } + + failNotify := make(chan bool) + // use this to synchronize on here + s.logFailure = func(supervisor *Supervisor, s Service, cf float64, ft float64, r bool, error interface{}, stacktrace []byte) { + failNotify <- r + } + + // All that setup was for this: Service1, please return now. + service1.take <- Fail + restarted := <-failNotify + <-service1.started + + if !restarted || s.failures != 1 || s.lastFail != pastVal { + t.Fatal("Did not fail in the expected manner") + } + // Getting past this means the service was restarted. + service1.take <- Happy + + // Service2, your turn. + service2.take <- Fail + nowFeeder.appendTimes(pastVal) + restarted = <-failNotify + <-service2.started + if !restarted || s.failures != 2 || s.lastFail != pastVal { + t.Fatal("Did not fail in the expected manner") + } + // And you're back. (That is, the correct service was restarted.) + service2.take <- Happy + + // Now, one failureDecay later, is everything working correctly? + oneDecayLater := time.Unix(1000030, 0) + nowFeeder.appendTimes(oneDecayLater) + service2.take <- Fail + restarted = <-failNotify + <-service2.started + // playing a bit fast and loose here with floating point, but... + // we get 2 by taking the current failure value of 2, decaying it + // by one interval, which cuts it in half to 1, then adding 1 again, + // all of which "should" be precise + if !restarted || s.failures != 2 || s.lastFail != oneDecayLater { + t.Fatal("Did not decay properly", s.lastFail, oneDecayLater) + } + + // For a change of pace, service1 would you be so kind as to panic? + nowFeeder.appendTimes(oneDecayLater) + service1.take <- Panic + restarted = <-failNotify + <-service1.started + if !restarted || s.failures != 3 || s.lastFail != oneDecayLater { + t.Fatal("Did not correctly recover from a panic") + } + + nowFeeder.appendTimes(oneDecayLater) + backingoff := make(chan bool) + s.logBackoff = func(s *Supervisor, backingOff bool) { + backingoff <- backingOff + } + + // And with this failure, we trigger the backoff code. + service1.take <- Fail + backoff := <-backingoff + restarted = <-failNotify + + if !backoff || restarted || s.failures != 4 { + t.Fatal("Broke past the threshold but did not log correctly", s.failures) + } + if service1.existing != 0 { + t.Fatal("service1 still exists according to itself?") + } + + // service2 is still running, because we don't shut anything down in a + // backoff, we just stop restarting. + service2.take <- Happy + + var correct bool + timer := time.NewTimer(time.Millisecond * 10) + // verify the service has not been restarted + // hard to get around race conditions here without simply using a timer... + select { + case service1.take <- Happy: + correct = false + case <-timer.C: + correct = true + } + if !correct { + t.Fatal("Restarted the service during the backoff interval") + } + + // tell the supervisor the restart interval has passed + resumeChan <- time.Time{} + backoff = <-backingoff + <-service1.started + s.sync() + if s.failures != 0 { + t.Fatal("Did not reset failure count after coming back from timeout.") + } + + nowFeeder.appendTimes(oneDecayLater) + service1.take <- Fail + restarted = <-failNotify + <-service1.started + if !restarted || backoff { + t.Fatal("For some reason, got that we were backing off again.", restarted, backoff) + } +} + +func TestRunningAlreadyRunning(t *testing.T) { + t.Parallel() + + s := NewSimple("A3") + go s.Serve() + defer s.Stop() + + // ensure the supervisor has made it to its main loop + s.sync() + var errored bool + func() { + defer func() { + if r := recover(); r != nil { + errored = true + } + }() + + s.Serve() + }() + if !errored { + t.Fatal("Supervisor failed to prevent itself from double-running.") + } +} + +func TestFullConstruction(t *testing.T) { + t.Parallel() + + s := New("Moo", Spec{ + Log: func(string) {}, + FailureDecay: 1, + FailureThreshold: 2, + FailureBackoff: 3, + Timeout: time.Second * 29, + }) + if s.String() != "Moo" || s.failureDecay != 1 || s.failureThreshold != 2 || s.failureBackoff != 3 || s.timeout != time.Second*29 { + t.Fatal("Full construction failed somehow") + } +} + +// This is mostly for coverage testing. +func TestDefaultLogging(t *testing.T) { + t.Parallel() + + s := NewSimple("A4") + + service := NewService("B4") + s.Add(service) + + s.failureThreshold = .5 + s.failureBackoff = time.Millisecond * 25 + go s.Serve() + s.sync() + + <-service.started + + resumeChan := make(chan time.Time) + s.getResume = func(d time.Duration) <-chan time.Time { + return resumeChan + } + + service.take <- UseStopChan + service.take <- Fail + <-service.stop + resumeChan <- time.Time{} + + <-service.started + + service.take <- Happy + + serviceName(&BarelyService{}) + + s.logBadStop(s, service) + s.logFailure(s, service, 1, 1, true, errors.New("test error"), []byte{}) + + s.Stop() +} + +func TestNestedSupervisors(t *testing.T) { + t.Parallel() + + super1 := NewSimple("Top5") + super2 := NewSimple("Nested5") + service := NewService("Service5") + + super2.logBadStop = func(*Supervisor, Service) { + panic("Failed to copy logBadStop") + } + + super1.Add(super2) + super2.Add(service) + + // test the functions got copied from super1; if this panics, it didn't + // get copied + super2.logBadStop(super2, service) + + go super1.Serve() + super1.sync() + + <-service.started + service.take <- Happy + + super1.Stop() +} + +func TestStoppingSupervisorStopsServices(t *testing.T) { + t.Parallel() + + s := NewSimple("Top6") + service := NewService("Service 6") + + s.Add(service) + + go s.Serve() + s.sync() + + <-service.started + + service.take <- UseStopChan + + s.Stop() + <-service.stop +} + +func TestStoppingStillWorksWithHungServices(t *testing.T) { + t.Parallel() + + s := NewSimple("Top7") + service := NewService("Service WillHang7") + + s.Add(service) + + go s.Serve() + + <-service.started + + service.take <- UseStopChan + service.take <- Hang + + resumeChan := make(chan time.Time) + s.getResume = func(d time.Duration) <-chan time.Time { + return resumeChan + } + failNotify := make(chan struct{}) + s.logBadStop = func(supervisor *Supervisor, s Service) { + failNotify <- struct{}{} + } + + s.Stop() + + resumeChan <- time.Time{} + <-failNotify + service.release <- true + <-service.stop +} + +func TestRemoveService(t *testing.T) { + t.Parallel() + + s := NewSimple("Top") + service := NewService("ServiceToRemove8") + + id := s.Add(service) + + go s.Serve() + + <-service.started + service.take <- UseStopChan + + err := s.Remove(id) + if err != nil { + t.Fatal("Removing service somehow failed") + } + <-service.stop + + err = s.Remove(ServiceToken{1<<36 + 1}) + if err != ErrWrongSupervisor { + t.Fatal("Did not detect that the ServiceToken was wrong") + } +} + +func TestFailureToConstruct(t *testing.T) { + t.Parallel() + + var s *Supervisor + + panics(func() { + s.Serve() + }) + + s = new(Supervisor) + panics(func() { + s.Serve() + }) +} + +func TestFailingSupervisors(t *testing.T) { + t.Parallel() + + // This is a bit of a complicated test, so let me explain what + // all this is doing: + // 1. Set up a top-level supervisor with a hair-trigger backoff. + // 2. Add a supervisor to that. + // 3. To that supervisor, add a service. + // 4. Panic the supervisor in the middle, sending the top-level into + // backoff. + // 5. Kill the lower level service too. + // 6. Verify that when the top-level service comes out of backoff, + // the service ends up restarted as expected. + + // Ultimately, we can't have more than a best-effort recovery here. + // A panic'ed supervisor can't really be trusted to have consistent state, + // and without *that*, we can't trust it to do anything sensible with + // the children it may have been running. So unlike Erlang, we can't + // can't really expect to be able to safely restart them or anything. + // Really, the "correct" answer is that the Supervisor must never panic, + // but in the event that it does, this verifies that it at least tries + // to get on with life. + + // This also tests that if a Supervisor itself panics, and one of its + // monitored services goes down in the meantime, that the monitored + // service also gets correctly restarted when the supervisor does. + + s1 := NewSimple("Top9") + s2 := NewSimple("Nested9") + service := NewService("Service9") + + s1.Add(s2) + s2.Add(service) + + go s1.Serve() + <-service.started + + s1.failureThreshold = .5 + + // let us control precisely when s1 comes back + resumeChan := make(chan time.Time) + s1.getResume = func(d time.Duration) <-chan time.Time { + return resumeChan + } + failNotify := make(chan string) + // use this to synchronize on here + s1.logFailure = func(supervisor *Supervisor, s Service, cf float64, ft float64, r bool, error interface{}, stacktrace []byte) { + failNotify <- fmt.Sprintf("%s", s) + } + + s2.panic() + + failing := <-failNotify + // that's enough sync to guarantee this: + if failing != "Nested9" || s1.state != paused { + t.Fatal("Top-level supervisor did not go into backoff as expected") + } + + service.take <- Fail + + resumeChan <- time.Time{} + <-service.started +} + +func TestNilSupervisorAdd(t *testing.T) { + t.Parallel() + + var s *Supervisor + + defer func() { + if r := recover(); r == nil { + t.Fatal("did not panic as expected on nil add") + } + }() + + s.Add(s) +} + +// https://github.com/thejerf/suture/issues/11 +// +// The purpose of this test is to verify that it does not cause data races, +// so there are no obvious assertions. +func TestIssue11(t *testing.T) { + t.Parallel() + + s := NewSimple("main") + s.ServeBackground() + + subsuper := NewSimple("sub") + s.Add(subsuper) + + subsuper.Add(NewService("may cause data race")) +} + +// http://golangtutorials.blogspot.com/2011/10/gotest-unit-testing-and-benchmarking-go.html +// claims test function are run in the same order as the source file... +// I'm not sure if this is part of the contract, though. Especially in the +// face of "t.Parallel()"... +func TestEverMultistarted(t *testing.T) { + if everMultistarted { + t.Fatal("Seem to have multistarted a service at some point, bummer.") + } +} + +// A test service that can be induced to fail, panic, or hang on demand. +func NewService(name string) *FailableService { + return &FailableService{name, make(chan bool), make(chan int), + make(chan bool, 1), make(chan bool), make(chan bool), 0} +} + +type FailableService struct { + name string + started chan bool + take chan int + shutdown chan bool + release chan bool + stop chan bool + existing int +} + +func (s *FailableService) Serve() { + if s.existing != 0 { + everMultistarted = true + panic("Multi-started the same service! " + s.name) + } + s.existing++ + + s.started <- true + + useStopChan := false + + for { + select { + case val := <-s.take: + switch val { + case Happy: + // Do nothing on purpose. Life is good! + case Fail: + s.existing-- + if useStopChan { + s.stop <- true + } + return + case Panic: + s.existing-- + panic("Panic!") + case Hang: + // or more specifically, "hang until I release you" + <-s.release + case UseStopChan: + useStopChan = true + } + case <-s.shutdown: + s.existing-- + if useStopChan { + s.stop <- true + } + return + } + } +} + +func (s *FailableService) String() string { + return s.name +} + +func (s *FailableService) Stop() { + s.shutdown <- true +} + +type NowFeeder struct { + values []time.Time + getter func() time.Time + m sync.Mutex +} + +// This is used to test serviceName; it's a service without a Stringer. +type BarelyService struct{} + +func (bs *BarelyService) Serve() {} +func (bs *BarelyService) Stop() {} + +func NewNowFeeder() (nf *NowFeeder) { + nf = new(NowFeeder) + nf.getter = func() time.Time { + nf.m.Lock() + defer nf.m.Unlock() + if len(nf.values) > 0 { + ret := nf.values[0] + nf.values = nf.values[1:] + return ret + } + panic("Ran out of values for NowFeeder") + } + return +} + +func (nf *NowFeeder) appendTimes(t ...time.Time) { + nf.m.Lock() + defer nf.m.Unlock() + nf.values = append(nf.values, t...) +} + +func panics(doesItPanic func()) (panics bool) { + defer func() { + if r := recover(); r != nil { + panics = true + } + }() + + doesItPanic() + + return +} diff --git a/Godeps/_workspace/src/golang.org/x/text/transform/examples_test.go b/Godeps/_workspace/src/golang.org/x/text/transform/examples_test.go new file mode 100644 index 000000000..f2e284dba --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/transform/examples_test.go @@ -0,0 +1,37 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package transform_test + +import ( + "fmt" + "unicode" + + "golang.org/x/text/transform" + "golang.org/x/text/unicode/norm" +) + +func ExampleRemoveFunc() { + input := []byte(`tschüß; до свидания`) + + b := make([]byte, len(input)) + + t := transform.RemoveFunc(unicode.IsSpace) + n, _, _ := t.Transform(b, input, true) + fmt.Println(string(b[:n])) + + t = transform.RemoveFunc(func(r rune) bool { + return !unicode.Is(unicode.Latin, r) + }) + n, _, _ = t.Transform(b, input, true) + fmt.Println(string(b[:n])) + + n, _, _ = t.Transform(b, norm.NFD.Bytes(input), true) + fmt.Println(string(b[:n])) + + // Output: + // tschüß;досвидания + // tschüß + // tschuß +} diff --git a/Godeps/_workspace/src/golang.org/x/text/transform/transform.go b/Godeps/_workspace/src/golang.org/x/text/transform/transform.go new file mode 100644 index 000000000..157ee7892 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/transform/transform.go @@ -0,0 +1,616 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package transform provides reader and writer wrappers that transform the +// bytes passing through as well as various transformations. Example +// transformations provided by other packages include normalization and +// conversion between character sets. +package transform + +import ( + "bytes" + "errors" + "io" + "unicode/utf8" +) + +var ( + // ErrShortDst means that the destination buffer was too short to + // receive all of the transformed bytes. + ErrShortDst = errors.New("transform: short destination buffer") + + // ErrShortSrc means that the source buffer has insufficient data to + // complete the transformation. + ErrShortSrc = errors.New("transform: short source buffer") + + // errInconsistentByteCount means that Transform returned success (nil + // error) but also returned nSrc inconsistent with the src argument. + errInconsistentByteCount = errors.New("transform: inconsistent byte count returned") + + // errShortInternal means that an internal buffer is not large enough + // to make progress and the Transform operation must be aborted. + errShortInternal = errors.New("transform: short internal buffer") +) + +// Transformer transforms bytes. +type Transformer interface { + // Transform writes to dst the transformed bytes read from src, and + // returns the number of dst bytes written and src bytes read. The + // atEOF argument tells whether src represents the last bytes of the + // input. + // + // Callers should always process the nDst bytes produced and account + // for the nSrc bytes consumed before considering the error err. + // + // A nil error means that all of the transformed bytes (whether freshly + // transformed from src or left over from previous Transform calls) + // were written to dst. A nil error can be returned regardless of + // whether atEOF is true. If err is nil then nSrc must equal len(src); + // the converse is not necessarily true. + // + // ErrShortDst means that dst was too short to receive all of the + // transformed bytes. ErrShortSrc means that src had insufficient data + // to complete the transformation. If both conditions apply, then + // either error may be returned. Other than the error conditions listed + // here, implementations are free to report other errors that arise. + Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) + + // Reset resets the state and allows a Transformer to be reused. + Reset() +} + +// NopResetter can be embedded by implementations of Transformer to add a nop +// Reset method. +type NopResetter struct{} + +// Reset implements the Reset method of the Transformer interface. +func (NopResetter) Reset() {} + +// Reader wraps another io.Reader by transforming the bytes read. +type Reader struct { + r io.Reader + t Transformer + err error + + // dst[dst0:dst1] contains bytes that have been transformed by t but + // not yet copied out via Read. + dst []byte + dst0, dst1 int + + // src[src0:src1] contains bytes that have been read from r but not + // yet transformed through t. + src []byte + src0, src1 int + + // transformComplete is whether the transformation is complete, + // regardless of whether or not it was successful. + transformComplete bool +} + +const defaultBufSize = 4096 + +// NewReader returns a new Reader that wraps r by transforming the bytes read +// via t. It calls Reset on t. +func NewReader(r io.Reader, t Transformer) *Reader { + t.Reset() + return &Reader{ + r: r, + t: t, + dst: make([]byte, defaultBufSize), + src: make([]byte, defaultBufSize), + } +} + +// Read implements the io.Reader interface. +func (r *Reader) Read(p []byte) (int, error) { + n, err := 0, error(nil) + for { + // Copy out any transformed bytes and return the final error if we are done. + if r.dst0 != r.dst1 { + n = copy(p, r.dst[r.dst0:r.dst1]) + r.dst0 += n + if r.dst0 == r.dst1 && r.transformComplete { + return n, r.err + } + return n, nil + } else if r.transformComplete { + return 0, r.err + } + + // Try to transform some source bytes, or to flush the transformer if we + // are out of source bytes. We do this even if r.r.Read returned an error. + // As the io.Reader documentation says, "process the n > 0 bytes returned + // before considering the error". + if r.src0 != r.src1 || r.err != nil { + r.dst0 = 0 + r.dst1, n, err = r.t.Transform(r.dst, r.src[r.src0:r.src1], r.err == io.EOF) + r.src0 += n + + switch { + case err == nil: + if r.src0 != r.src1 { + r.err = errInconsistentByteCount + } + // The Transform call was successful; we are complete if we + // cannot read more bytes into src. + r.transformComplete = r.err != nil + continue + case err == ErrShortDst && (r.dst1 != 0 || n != 0): + // Make room in dst by copying out, and try again. + continue + case err == ErrShortSrc && r.src1-r.src0 != len(r.src) && r.err == nil: + // Read more bytes into src via the code below, and try again. + default: + r.transformComplete = true + // The reader error (r.err) takes precedence over the + // transformer error (err) unless r.err is nil or io.EOF. + if r.err == nil || r.err == io.EOF { + r.err = err + } + continue + } + } + + // Move any untransformed source bytes to the start of the buffer + // and read more bytes. + if r.src0 != 0 { + r.src0, r.src1 = 0, copy(r.src, r.src[r.src0:r.src1]) + } + n, r.err = r.r.Read(r.src[r.src1:]) + r.src1 += n + } +} + +// TODO: implement ReadByte (and ReadRune??). + +// Writer wraps another io.Writer by transforming the bytes read. +// The user needs to call Close to flush unwritten bytes that may +// be buffered. +type Writer struct { + w io.Writer + t Transformer + dst []byte + + // src[:n] contains bytes that have not yet passed through t. + src []byte + n int +} + +// NewWriter returns a new Writer that wraps w by transforming the bytes written +// via t. It calls Reset on t. +func NewWriter(w io.Writer, t Transformer) *Writer { + t.Reset() + return &Writer{ + w: w, + t: t, + dst: make([]byte, defaultBufSize), + src: make([]byte, defaultBufSize), + } +} + +// Write implements the io.Writer interface. If there are not enough +// bytes available to complete a Transform, the bytes will be buffered +// for the next write. Call Close to convert the remaining bytes. +func (w *Writer) Write(data []byte) (n int, err error) { + src := data + if w.n > 0 { + // Append bytes from data to the last remainder. + // TODO: limit the amount copied on first try. + n = copy(w.src[w.n:], data) + w.n += n + src = w.src[:w.n] + } + for { + nDst, nSrc, err := w.t.Transform(w.dst, src, false) + if _, werr := w.w.Write(w.dst[:nDst]); werr != nil { + return n, werr + } + src = src[nSrc:] + if w.n > 0 && len(src) <= n { + // Enough bytes from w.src have been consumed. We make src point + // to data instead to reduce the copying. + w.n = 0 + n -= len(src) + src = data[n:] + if n < len(data) && (err == nil || err == ErrShortSrc) { + continue + } + } else { + n += nSrc + } + switch { + case err == ErrShortDst && (nDst > 0 || nSrc > 0): + case err == ErrShortSrc && len(src) < len(w.src): + m := copy(w.src, src) + // If w.n > 0, bytes from data were already copied to w.src and n + // was already set to the number of bytes consumed. + if w.n == 0 { + n += m + } + w.n = m + return n, nil + case err == nil && w.n > 0: + return n, errInconsistentByteCount + default: + return n, err + } + } +} + +// Close implements the io.Closer interface. +func (w *Writer) Close() error { + for src := w.src[:w.n]; len(src) > 0; { + nDst, nSrc, err := w.t.Transform(w.dst, src, true) + if nDst == 0 { + return err + } + if _, werr := w.w.Write(w.dst[:nDst]); werr != nil { + return werr + } + if err != ErrShortDst { + return err + } + src = src[nSrc:] + } + return nil +} + +type nop struct{ NopResetter } + +func (nop) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + n := copy(dst, src) + if n < len(src) { + err = ErrShortDst + } + return n, n, err +} + +type discard struct{ NopResetter } + +func (discard) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + return 0, len(src), nil +} + +var ( + // Discard is a Transformer for which all Transform calls succeed + // by consuming all bytes and writing nothing. + Discard Transformer = discard{} + + // Nop is a Transformer that copies src to dst. + Nop Transformer = nop{} +) + +// chain is a sequence of links. A chain with N Transformers has N+1 links and +// N+1 buffers. Of those N+1 buffers, the first and last are the src and dst +// buffers given to chain.Transform and the middle N-1 buffers are intermediate +// buffers owned by the chain. The i'th link transforms bytes from the i'th +// buffer chain.link[i].b at read offset chain.link[i].p to the i+1'th buffer +// chain.link[i+1].b at write offset chain.link[i+1].n, for i in [0, N). +type chain struct { + link []link + err error + // errStart is the index at which the error occurred plus 1. Processing + // errStart at this level at the next call to Transform. As long as + // errStart > 0, chain will not consume any more source bytes. + errStart int +} + +func (c *chain) fatalError(errIndex int, err error) { + if i := errIndex + 1; i > c.errStart { + c.errStart = i + c.err = err + } +} + +type link struct { + t Transformer + // b[p:n] holds the bytes to be transformed by t. + b []byte + p int + n int +} + +func (l *link) src() []byte { + return l.b[l.p:l.n] +} + +func (l *link) dst() []byte { + return l.b[l.n:] +} + +// Chain returns a Transformer that applies t in sequence. +func Chain(t ...Transformer) Transformer { + if len(t) == 0 { + return nop{} + } + c := &chain{link: make([]link, len(t)+1)} + for i, tt := range t { + c.link[i].t = tt + } + // Allocate intermediate buffers. + b := make([][defaultBufSize]byte, len(t)-1) + for i := range b { + c.link[i+1].b = b[i][:] + } + return c +} + +// Reset resets the state of Chain. It calls Reset on all the Transformers. +func (c *chain) Reset() { + for i, l := range c.link { + if l.t != nil { + l.t.Reset() + } + c.link[i].p, c.link[i].n = 0, 0 + } +} + +// Transform applies the transformers of c in sequence. +func (c *chain) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + // Set up src and dst in the chain. + srcL := &c.link[0] + dstL := &c.link[len(c.link)-1] + srcL.b, srcL.p, srcL.n = src, 0, len(src) + dstL.b, dstL.n = dst, 0 + var lastFull, needProgress bool // for detecting progress + + // i is the index of the next Transformer to apply, for i in [low, high]. + // low is the lowest index for which c.link[low] may still produce bytes. + // high is the highest index for which c.link[high] has a Transformer. + // The error returned by Transform determines whether to increase or + // decrease i. We try to completely fill a buffer before converting it. + for low, i, high := c.errStart, c.errStart, len(c.link)-2; low <= i && i <= high; { + in, out := &c.link[i], &c.link[i+1] + nDst, nSrc, err0 := in.t.Transform(out.dst(), in.src(), atEOF && low == i) + out.n += nDst + in.p += nSrc + if i > 0 && in.p == in.n { + in.p, in.n = 0, 0 + } + needProgress, lastFull = lastFull, false + switch err0 { + case ErrShortDst: + // Process the destination buffer next. Return if we are already + // at the high index. + if i == high { + return dstL.n, srcL.p, ErrShortDst + } + if out.n != 0 { + i++ + // If the Transformer at the next index is not able to process any + // source bytes there is nothing that can be done to make progress + // and the bytes will remain unprocessed. lastFull is used to + // detect this and break out of the loop with a fatal error. + lastFull = true + continue + } + // The destination buffer was too small, but is completely empty. + // Return a fatal error as this transformation can never complete. + c.fatalError(i, errShortInternal) + case ErrShortSrc: + if i == 0 { + // Save ErrShortSrc in err. All other errors take precedence. + err = ErrShortSrc + break + } + // Source bytes were depleted before filling up the destination buffer. + // Verify we made some progress, move the remaining bytes to the errStart + // and try to get more source bytes. + if needProgress && nSrc == 0 || in.n-in.p == len(in.b) { + // There were not enough source bytes to proceed while the source + // buffer cannot hold any more bytes. Return a fatal error as this + // transformation can never complete. + c.fatalError(i, errShortInternal) + break + } + // in.b is an internal buffer and we can make progress. + in.p, in.n = 0, copy(in.b, in.src()) + fallthrough + case nil: + // if i == low, we have depleted the bytes at index i or any lower levels. + // In that case we increase low and i. In all other cases we decrease i to + // fetch more bytes before proceeding to the next index. + if i > low { + i-- + continue + } + default: + c.fatalError(i, err0) + } + // Exhausted level low or fatal error: increase low and continue + // to process the bytes accepted so far. + i++ + low = i + } + + // If c.errStart > 0, this means we found a fatal error. We will clear + // all upstream buffers. At this point, no more progress can be made + // downstream, as Transform would have bailed while handling ErrShortDst. + if c.errStart > 0 { + for i := 1; i < c.errStart; i++ { + c.link[i].p, c.link[i].n = 0, 0 + } + err, c.errStart, c.err = c.err, 0, nil + } + return dstL.n, srcL.p, err +} + +// RemoveFunc returns a Transformer that removes from the input all runes r for +// which f(r) is true. Illegal bytes in the input are replaced by RuneError. +func RemoveFunc(f func(r rune) bool) Transformer { + return removeF(f) +} + +type removeF func(r rune) bool + +func (removeF) Reset() {} + +// Transform implements the Transformer interface. +func (t removeF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + for r, sz := rune(0), 0; len(src) > 0; src = src[sz:] { + + if r = rune(src[0]); r < utf8.RuneSelf { + sz = 1 + } else { + r, sz = utf8.DecodeRune(src) + + if sz == 1 { + // Invalid rune. + if !atEOF && !utf8.FullRune(src) { + err = ErrShortSrc + break + } + // We replace illegal bytes with RuneError. Not doing so might + // otherwise turn a sequence of invalid UTF-8 into valid UTF-8. + // The resulting byte sequence may subsequently contain runes + // for which t(r) is true that were passed unnoticed. + if !t(r) { + if nDst+3 > len(dst) { + err = ErrShortDst + break + } + nDst += copy(dst[nDst:], "\uFFFD") + } + nSrc++ + continue + } + } + + if !t(r) { + if nDst+sz > len(dst) { + err = ErrShortDst + break + } + nDst += copy(dst[nDst:], src[:sz]) + } + nSrc += sz + } + return +} + +// grow returns a new []byte that is longer than b, and copies the first n bytes +// of b to the start of the new slice. +func grow(b []byte, n int) []byte { + m := len(b) + if m <= 256 { + m *= 2 + } else { + m += m >> 1 + } + buf := make([]byte, m) + copy(buf, b[:n]) + return buf +} + +const initialBufSize = 128 + +// String returns a string with the result of converting s[:n] using t, where +// n <= len(s). If err == nil, n will be len(s). It calls Reset on t. +func String(t Transformer, s string) (result string, n int, err error) { + if s == "" { + return "", 0, nil + } + + t.Reset() + + // Allocate only once. Note that both dst and src escape when passed to + // Transform. + buf := [2 * initialBufSize]byte{} + dst := buf[:initialBufSize:initialBufSize] + src := buf[initialBufSize : 2*initialBufSize] + + // Avoid allocation if the transformed string is identical to the original. + // After this loop, pDst will point to the furthest point in s for which it + // could be detected that t gives equal results, src[:nSrc] will + // indicated the last processed chunk of s for which the output is not equal + // and dst[:nDst] will be the transform of this chunk. + var nDst, nSrc int + pDst := 0 // Used as index in both src and dst in this loop. + for { + n := copy(src, s[pDst:]) + nDst, nSrc, err = t.Transform(dst, src[:n], pDst+n == len(s)) + + // Note 1: we will not enter the loop with pDst == len(s) and we will + // not end the loop with it either. So if nSrc is 0, this means there is + // some kind of error from which we cannot recover given the current + // buffer sizes. We will give up in this case. + // Note 2: it is not entirely correct to simply do a bytes.Equal as + // a Transformer may buffer internally. It will work in most cases, + // though, and no harm is done if it doesn't work. + // TODO: let transformers implement an optional Spanner interface, akin + // to norm's QuickSpan. This would even allow us to avoid any allocation. + if nSrc == 0 || !bytes.Equal(dst[:nDst], src[:nSrc]) { + break + } + + if pDst += nDst; pDst == len(s) { + return s, pDst, nil + } + } + + // Move the bytes seen so far to dst. + pSrc := pDst + nSrc + if pDst+nDst <= initialBufSize { + copy(dst[pDst:], dst[:nDst]) + } else { + b := make([]byte, len(s)+nDst-nSrc) + copy(b[pDst:], dst[:nDst]) + dst = b + } + copy(dst, s[:pDst]) + pDst += nDst + + if err != nil && err != ErrShortDst && err != ErrShortSrc { + return string(dst[:pDst]), pSrc, err + } + + // Complete the string with the remainder. + for { + n := copy(src, s[pSrc:]) + nDst, nSrc, err = t.Transform(dst[pDst:], src[:n], pSrc+n == len(s)) + pDst += nDst + pSrc += nSrc + + switch err { + case nil: + if pSrc == len(s) { + return string(dst[:pDst]), pSrc, nil + } + case ErrShortDst: + // Do not grow as long as we can make progress. This may avoid + // excessive allocations. + if nDst == 0 { + dst = grow(dst, pDst) + } + case ErrShortSrc: + if nSrc == 0 { + src = grow(src, 0) + } + default: + return string(dst[:pDst]), pSrc, err + } + } +} + +// Bytes returns a new byte slice with the result of converting b[:n] using t, +// where n <= len(b). If err == nil, n will be len(b). It calls Reset on t. +func Bytes(t Transformer, b []byte) (result []byte, n int, err error) { + t.Reset() + dst := make([]byte, len(b)) + pDst, pSrc := 0, 0 + for { + nDst, nSrc, err := t.Transform(dst[pDst:], b[pSrc:], true) + pDst += nDst + pSrc += nSrc + if err != ErrShortDst { + return dst[:pDst], pSrc, err + } + + // Grow the destination buffer, but do not grow as long as we can make + // progress. This may avoid excessive allocations. + if nDst == 0 { + dst = grow(dst, pDst) + } + } +} diff --git a/Godeps/_workspace/src/golang.org/x/text/transform/transform_test.go b/Godeps/_workspace/src/golang.org/x/text/transform/transform_test.go new file mode 100644 index 000000000..b463f4f1b --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/transform/transform_test.go @@ -0,0 +1,1082 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package transform + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "strconv" + "strings" + "testing" + "time" + "unicode/utf8" +) + +type lowerCaseASCII struct{ NopResetter } + +func (lowerCaseASCII) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + n := len(src) + if n > len(dst) { + n, err = len(dst), ErrShortDst + } + for i, c := range src[:n] { + if 'A' <= c && c <= 'Z' { + c += 'a' - 'A' + } + dst[i] = c + } + return n, n, err +} + +var errYouMentionedX = errors.New("you mentioned X") + +type dontMentionX struct{ NopResetter } + +func (dontMentionX) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + n := len(src) + if n > len(dst) { + n, err = len(dst), ErrShortDst + } + for i, c := range src[:n] { + if c == 'X' { + return i, i, errYouMentionedX + } + dst[i] = c + } + return n, n, err +} + +// doublerAtEOF is a strange Transformer that transforms "this" to "tthhiiss", +// but only if atEOF is true. +type doublerAtEOF struct{ NopResetter } + +func (doublerAtEOF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + if !atEOF { + return 0, 0, ErrShortSrc + } + for i, c := range src { + if 2*i+2 >= len(dst) { + return 2 * i, i, ErrShortDst + } + dst[2*i+0] = c + dst[2*i+1] = c + } + return 2 * len(src), len(src), nil +} + +// rleDecode and rleEncode implement a toy run-length encoding: "aabbbbbbbbbb" +// is encoded as "2a10b". The decoding is assumed to not contain any numbers. + +type rleDecode struct{ NopResetter } + +func (rleDecode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { +loop: + for len(src) > 0 { + n := 0 + for i, c := range src { + if '0' <= c && c <= '9' { + n = 10*n + int(c-'0') + continue + } + if i == 0 { + return nDst, nSrc, errors.New("rleDecode: bad input") + } + if n > len(dst) { + return nDst, nSrc, ErrShortDst + } + for j := 0; j < n; j++ { + dst[j] = c + } + dst, src = dst[n:], src[i+1:] + nDst, nSrc = nDst+n, nSrc+i+1 + continue loop + } + if atEOF { + return nDst, nSrc, errors.New("rleDecode: bad input") + } + return nDst, nSrc, ErrShortSrc + } + return nDst, nSrc, nil +} + +type rleEncode struct { + NopResetter + + // allowStutter means that "xxxxxxxx" can be encoded as "5x3x" + // instead of always as "8x". + allowStutter bool +} + +func (e rleEncode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + for len(src) > 0 { + n, c0 := len(src), src[0] + for i, c := range src[1:] { + if c != c0 { + n = i + 1 + break + } + } + if n == len(src) && !atEOF && !e.allowStutter { + return nDst, nSrc, ErrShortSrc + } + s := strconv.Itoa(n) + if len(s) >= len(dst) { + return nDst, nSrc, ErrShortDst + } + copy(dst, s) + dst[len(s)] = c0 + dst, src = dst[len(s)+1:], src[n:] + nDst, nSrc = nDst+len(s)+1, nSrc+n + } + return nDst, nSrc, nil +} + +// trickler consumes all input bytes, but writes a single byte at a time to dst. +type trickler []byte + +func (t *trickler) Reset() { + *t = nil +} + +func (t *trickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + *t = append(*t, src...) + if len(*t) == 0 { + return 0, 0, nil + } + if len(dst) == 0 { + return 0, len(src), ErrShortDst + } + dst[0] = (*t)[0] + *t = (*t)[1:] + if len(*t) > 0 { + err = ErrShortDst + } + return 1, len(src), err +} + +// delayedTrickler is like trickler, but delays writing output to dst. This is +// highly unlikely to be relevant in practice, but it seems like a good idea +// to have some tolerance as long as progress can be detected. +type delayedTrickler []byte + +func (t *delayedTrickler) Reset() { + *t = nil +} +func (t *delayedTrickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + if len(*t) > 0 && len(dst) > 0 { + dst[0] = (*t)[0] + *t = (*t)[1:] + nDst = 1 + } + *t = append(*t, src...) + if len(*t) > 0 { + err = ErrShortDst + } + return nDst, len(src), err +} + +type testCase struct { + desc string + t Transformer + src string + dstSize int + srcSize int + ioSize int + wantStr string + wantErr error + wantIter int // number of iterations taken; 0 means we don't care. +} + +func (t testCase) String() string { + return tstr(t.t) + "; " + t.desc +} + +func tstr(t Transformer) string { + if stringer, ok := t.(fmt.Stringer); ok { + return stringer.String() + } + s := fmt.Sprintf("%T", t) + return s[1+strings.Index(s, "."):] +} + +func (c chain) String() string { + buf := &bytes.Buffer{} + buf.WriteString("Chain(") + for i, l := range c.link[:len(c.link)-1] { + if i != 0 { + fmt.Fprint(buf, ", ") + } + buf.WriteString(tstr(l.t)) + } + buf.WriteString(")") + return buf.String() +} + +var testCases = []testCase{ + { + desc: "empty", + t: lowerCaseASCII{}, + src: "", + dstSize: 100, + srcSize: 100, + wantStr: "", + }, + + { + desc: "basic", + t: lowerCaseASCII{}, + src: "Hello WORLD.", + dstSize: 100, + srcSize: 100, + wantStr: "hello world.", + }, + + { + desc: "small dst", + t: lowerCaseASCII{}, + src: "Hello WORLD.", + dstSize: 3, + srcSize: 100, + wantStr: "hello world.", + }, + + { + desc: "small src", + t: lowerCaseASCII{}, + src: "Hello WORLD.", + dstSize: 100, + srcSize: 4, + wantStr: "hello world.", + }, + + { + desc: "small buffers", + t: lowerCaseASCII{}, + src: "Hello WORLD.", + dstSize: 3, + srcSize: 4, + wantStr: "hello world.", + }, + + { + desc: "very small buffers", + t: lowerCaseASCII{}, + src: "Hello WORLD.", + dstSize: 1, + srcSize: 1, + wantStr: "hello world.", + }, + + { + desc: "basic", + t: dontMentionX{}, + src: "The First Rule of Transform Club: don't mention Mister X, ever.", + dstSize: 100, + srcSize: 100, + wantStr: "The First Rule of Transform Club: don't mention Mister ", + wantErr: errYouMentionedX, + }, + + { + desc: "small buffers", + t: dontMentionX{}, + src: "The First Rule of Transform Club: don't mention Mister X, ever.", + dstSize: 10, + srcSize: 10, + wantStr: "The First Rule of Transform Club: don't mention Mister ", + wantErr: errYouMentionedX, + }, + + { + desc: "very small buffers", + t: dontMentionX{}, + src: "The First Rule of Transform Club: don't mention Mister X, ever.", + dstSize: 1, + srcSize: 1, + wantStr: "The First Rule of Transform Club: don't mention Mister ", + wantErr: errYouMentionedX, + }, + + { + desc: "only transform at EOF", + t: doublerAtEOF{}, + src: "this", + dstSize: 100, + srcSize: 100, + wantStr: "tthhiiss", + }, + + { + desc: "basic", + t: rleDecode{}, + src: "1a2b3c10d11e0f1g", + dstSize: 100, + srcSize: 100, + wantStr: "abbcccddddddddddeeeeeeeeeeeg", + }, + + { + desc: "long", + t: rleDecode{}, + src: "12a23b34c45d56e99z", + dstSize: 100, + srcSize: 100, + wantStr: strings.Repeat("a", 12) + + strings.Repeat("b", 23) + + strings.Repeat("c", 34) + + strings.Repeat("d", 45) + + strings.Repeat("e", 56) + + strings.Repeat("z", 99), + }, + + { + desc: "tight buffers", + t: rleDecode{}, + src: "1a2b3c10d11e0f1g", + dstSize: 11, + srcSize: 3, + wantStr: "abbcccddddddddddeeeeeeeeeeeg", + }, + + { + desc: "short dst", + t: rleDecode{}, + src: "1a2b3c10d11e0f1g", + dstSize: 10, + srcSize: 3, + wantStr: "abbcccdddddddddd", + wantErr: ErrShortDst, + }, + + { + desc: "short src", + t: rleDecode{}, + src: "1a2b3c10d11e0f1g", + dstSize: 11, + srcSize: 2, + ioSize: 2, + wantStr: "abbccc", + wantErr: ErrShortSrc, + }, + + { + desc: "basic", + t: rleEncode{}, + src: "abbcccddddddddddeeeeeeeeeeeg", + dstSize: 100, + srcSize: 100, + wantStr: "1a2b3c10d11e1g", + }, + + { + desc: "long", + t: rleEncode{}, + src: strings.Repeat("a", 12) + + strings.Repeat("b", 23) + + strings.Repeat("c", 34) + + strings.Repeat("d", 45) + + strings.Repeat("e", 56) + + strings.Repeat("z", 99), + dstSize: 100, + srcSize: 100, + wantStr: "12a23b34c45d56e99z", + }, + + { + desc: "tight buffers", + t: rleEncode{}, + src: "abbcccddddddddddeeeeeeeeeeeg", + dstSize: 3, + srcSize: 12, + wantStr: "1a2b3c10d11e1g", + }, + + { + desc: "short dst", + t: rleEncode{}, + src: "abbcccddddddddddeeeeeeeeeeeg", + dstSize: 2, + srcSize: 12, + wantStr: "1a2b3c", + wantErr: ErrShortDst, + }, + + { + desc: "short src", + t: rleEncode{}, + src: "abbcccddddddddddeeeeeeeeeeeg", + dstSize: 3, + srcSize: 11, + ioSize: 11, + wantStr: "1a2b3c10d", + wantErr: ErrShortSrc, + }, + + { + desc: "allowStutter = false", + t: rleEncode{allowStutter: false}, + src: "aaaabbbbbbbbccccddddd", + dstSize: 10, + srcSize: 10, + wantStr: "4a8b4c5d", + }, + + { + desc: "allowStutter = true", + t: rleEncode{allowStutter: true}, + src: "aaaabbbbbbbbccccddddd", + dstSize: 10, + srcSize: 10, + ioSize: 10, + wantStr: "4a6b2b4c4d1d", + }, + + { + desc: "trickler", + t: &trickler{}, + src: "abcdefghijklm", + dstSize: 3, + srcSize: 15, + wantStr: "abcdefghijklm", + }, + + { + desc: "delayedTrickler", + t: &delayedTrickler{}, + src: "abcdefghijklm", + dstSize: 3, + srcSize: 15, + wantStr: "abcdefghijklm", + }, +} + +func TestReader(t *testing.T) { + for _, tc := range testCases { + r := NewReader(strings.NewReader(tc.src), tc.t) + // Differently sized dst and src buffers are not part of the + // exported API. We override them manually. + r.dst = make([]byte, tc.dstSize) + r.src = make([]byte, tc.srcSize) + got, err := ioutil.ReadAll(r) + str := string(got) + if str != tc.wantStr || err != tc.wantErr { + t.Errorf("%s:\ngot %q, %v\nwant %q, %v", tc, str, err, tc.wantStr, tc.wantErr) + } + } +} + +func TestWriter(t *testing.T) { + tests := append(testCases, chainTests()...) + for _, tc := range tests { + sizes := []int{1, 2, 3, 4, 5, 10, 100, 1000} + if tc.ioSize > 0 { + sizes = []int{tc.ioSize} + } + for _, sz := range sizes { + bb := &bytes.Buffer{} + w := NewWriter(bb, tc.t) + // Differently sized dst and src buffers are not part of the + // exported API. We override them manually. + w.dst = make([]byte, tc.dstSize) + w.src = make([]byte, tc.srcSize) + src := make([]byte, sz) + var err error + for b := tc.src; len(b) > 0 && err == nil; { + n := copy(src, b) + b = b[n:] + m := 0 + m, err = w.Write(src[:n]) + if m != n && err == nil { + t.Errorf("%s:%d: did not consume all bytes %d < %d", tc, sz, m, n) + } + } + if err == nil { + err = w.Close() + } + str := bb.String() + if str != tc.wantStr || err != tc.wantErr { + t.Errorf("%s:%d:\ngot %q, %v\nwant %q, %v", tc, sz, str, err, tc.wantStr, tc.wantErr) + } + } + } +} + +func TestNop(t *testing.T) { + testCases := []struct { + str string + dstSize int + err error + }{ + {"", 0, nil}, + {"", 10, nil}, + {"a", 0, ErrShortDst}, + {"a", 1, nil}, + {"a", 10, nil}, + } + for i, tc := range testCases { + dst := make([]byte, tc.dstSize) + nDst, nSrc, err := Nop.Transform(dst, []byte(tc.str), true) + want := tc.str + if tc.dstSize < len(want) { + want = want[:tc.dstSize] + } + if got := string(dst[:nDst]); got != want || err != tc.err || nSrc != nDst { + t.Errorf("%d:\ngot %q, %d, %v\nwant %q, %d, %v", i, got, nSrc, err, want, nDst, tc.err) + } + } +} + +func TestDiscard(t *testing.T) { + testCases := []struct { + str string + dstSize int + }{ + {"", 0}, + {"", 10}, + {"a", 0}, + {"ab", 10}, + } + for i, tc := range testCases { + nDst, nSrc, err := Discard.Transform(make([]byte, tc.dstSize), []byte(tc.str), true) + if nDst != 0 || nSrc != len(tc.str) || err != nil { + t.Errorf("%d:\ngot %q, %d, %v\nwant 0, %d, nil", i, nDst, nSrc, err, len(tc.str)) + } + } +} + +// mkChain creates a Chain transformer. x must be alternating between transformer +// and bufSize, like T, (sz, T)* +func mkChain(x ...interface{}) *chain { + t := []Transformer{} + for i := 0; i < len(x); i += 2 { + t = append(t, x[i].(Transformer)) + } + c := Chain(t...).(*chain) + for i, j := 1, 1; i < len(x); i, j = i+2, j+1 { + c.link[j].b = make([]byte, x[i].(int)) + } + return c +} + +func chainTests() []testCase { + return []testCase{ + { + desc: "nil error", + t: mkChain(rleEncode{}, 100, lowerCaseASCII{}), + src: "ABB", + dstSize: 100, + srcSize: 100, + wantStr: "1a2b", + wantErr: nil, + wantIter: 1, + }, + + { + desc: "short dst buffer", + t: mkChain(lowerCaseASCII{}, 3, rleDecode{}), + src: "1a2b3c10d11e0f1g", + dstSize: 10, + srcSize: 3, + wantStr: "abbcccdddddddddd", + wantErr: ErrShortDst, + }, + + { + desc: "short internal dst buffer", + t: mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop), + src: "1a2b3c10d11e0f1g", + dstSize: 100, + srcSize: 3, + wantStr: "abbcccdddddddddd", + wantErr: errShortInternal, + }, + + { + desc: "short internal dst buffer from input", + t: mkChain(rleDecode{}, 10, Nop), + src: "1a2b3c10d11e0f1g", + dstSize: 100, + srcSize: 3, + wantStr: "abbcccdddddddddd", + wantErr: errShortInternal, + }, + + { + desc: "empty short internal dst buffer", + t: mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop), + src: "4a7b11e0f1g", + dstSize: 100, + srcSize: 3, + wantStr: "aaaabbbbbbb", + wantErr: errShortInternal, + }, + + { + desc: "empty short internal dst buffer from input", + t: mkChain(rleDecode{}, 10, Nop), + src: "4a7b11e0f1g", + dstSize: 100, + srcSize: 3, + wantStr: "aaaabbbbbbb", + wantErr: errShortInternal, + }, + + { + desc: "short internal src buffer after full dst buffer", + t: mkChain(Nop, 5, rleEncode{}, 10, Nop), + src: "cccccddddd", + dstSize: 100, + srcSize: 100, + wantStr: "", + wantErr: errShortInternal, + wantIter: 1, + }, + + { + desc: "short internal src buffer after short dst buffer; test lastFull", + t: mkChain(rleDecode{}, 5, rleEncode{}, 4, Nop), + src: "2a1b4c6d", + dstSize: 100, + srcSize: 100, + wantStr: "2a1b", + wantErr: errShortInternal, + }, + + { + desc: "short internal src buffer after successful complete fill", + t: mkChain(Nop, 3, rleDecode{}), + src: "123a4b", + dstSize: 4, + srcSize: 3, + wantStr: "", + wantErr: errShortInternal, + wantIter: 1, + }, + + { + desc: "short internal src buffer after short dst buffer; test lastFull", + t: mkChain(rleDecode{}, 5, rleEncode{}), + src: "2a1b4c6d", + dstSize: 4, + srcSize: 100, + wantStr: "2a1b", + wantErr: errShortInternal, + }, + + { + desc: "short src buffer", + t: mkChain(rleEncode{}, 5, Nop), + src: "abbcccddddeeeee", + dstSize: 4, + srcSize: 4, + ioSize: 4, + wantStr: "1a2b3c", + wantErr: ErrShortSrc, + }, + + { + desc: "process all in one go", + t: mkChain(rleEncode{}, 5, Nop), + src: "abbcccddddeeeeeffffff", + dstSize: 100, + srcSize: 100, + wantStr: "1a2b3c4d5e6f", + wantErr: nil, + wantIter: 1, + }, + + { + desc: "complete processing downstream after error", + t: mkChain(dontMentionX{}, 2, rleDecode{}, 5, Nop), + src: "3a4b5eX", + dstSize: 100, + srcSize: 100, + ioSize: 100, + wantStr: "aaabbbbeeeee", + wantErr: errYouMentionedX, + }, + + { + desc: "return downstream fatal errors first (followed by short dst)", + t: mkChain(dontMentionX{}, 8, rleDecode{}, 4, Nop), + src: "3a4b5eX", + dstSize: 100, + srcSize: 100, + ioSize: 100, + wantStr: "aaabbbb", + wantErr: errShortInternal, + }, + + { + desc: "return downstream fatal errors first (followed by short src)", + t: mkChain(dontMentionX{}, 5, Nop, 1, rleDecode{}), + src: "1a5bX", + dstSize: 100, + srcSize: 100, + ioSize: 100, + wantStr: "", + wantErr: errShortInternal, + }, + + { + desc: "short internal", + t: mkChain(Nop, 11, rleEncode{}, 3, Nop), + src: "abbcccddddddddddeeeeeeeeeeeg", + dstSize: 3, + srcSize: 100, + wantStr: "1a2b3c10d", + wantErr: errShortInternal, + }, + } +} + +func doTransform(tc testCase) (res string, iter int, err error) { + tc.t.Reset() + dst := make([]byte, tc.dstSize) + out, in := make([]byte, 0, 2*len(tc.src)), []byte(tc.src) + for { + iter++ + src, atEOF := in, true + if len(src) > tc.srcSize { + src, atEOF = src[:tc.srcSize], false + } + nDst, nSrc, err := tc.t.Transform(dst, src, atEOF) + out = append(out, dst[:nDst]...) + in = in[nSrc:] + switch { + case err == nil && len(in) != 0: + case err == ErrShortSrc && nSrc > 0: + case err == ErrShortDst && (nDst > 0 || nSrc > 0): + default: + return string(out), iter, err + } + } +} + +func TestChain(t *testing.T) { + if c, ok := Chain().(nop); !ok { + t.Errorf("empty chain: %v; want Nop", c) + } + + // Test Chain for a single Transformer. + for _, tc := range testCases { + tc.t = Chain(tc.t) + str, _, err := doTransform(tc) + if str != tc.wantStr || err != tc.wantErr { + t.Errorf("%s:\ngot %q, %v\nwant %q, %v", tc, str, err, tc.wantStr, tc.wantErr) + } + } + + tests := chainTests() + sizes := []int{1, 2, 3, 4, 5, 7, 10, 100, 1000} + addTest := func(tc testCase, t *chain) { + if t.link[0].t != tc.t && tc.wantErr == ErrShortSrc { + tc.wantErr = errShortInternal + } + if t.link[len(t.link)-2].t != tc.t && tc.wantErr == ErrShortDst { + tc.wantErr = errShortInternal + } + tc.t = t + tests = append(tests, tc) + } + for _, tc := range testCases { + for _, sz := range sizes { + tt := tc + tt.dstSize = sz + addTest(tt, mkChain(tc.t, tc.dstSize, Nop)) + addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 2, Nop)) + addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop)) + if sz >= tc.dstSize && (tc.wantErr != ErrShortDst || sz == tc.dstSize) { + addTest(tt, mkChain(Nop, tc.srcSize, tc.t)) + addTest(tt, mkChain(Nop, 100, Nop, tc.srcSize, tc.t)) + } + } + } + for _, tc := range testCases { + tt := tc + tt.dstSize = 1 + tt.wantStr = "" + addTest(tt, mkChain(tc.t, tc.dstSize, Discard)) + addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Discard)) + addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, tc.dstSize, Discard)) + } + for _, tc := range testCases { + tt := tc + tt.dstSize = 100 + tt.wantStr = strings.Replace(tc.src, "0f", "", -1) + // Chain encoders and decoders. + if _, ok := tc.t.(rleEncode); ok && tc.wantErr == nil { + addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 1000, rleDecode{})) + addTest(tt, mkChain(tc.t, tc.dstSize, Nop, tc.dstSize, rleDecode{})) + addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleDecode{})) + // decoding needs larger destinations + addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, rleDecode{}, 100, Nop)) + addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleDecode{}, 100, Nop)) + } else if _, ok := tc.t.(rleDecode); ok && tc.wantErr == nil { + // The internal buffer size may need to be the sum of the maximum segment + // size of the two encoders! + addTest(tt, mkChain(tc.t, 2*tc.dstSize, rleEncode{})) + addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 101, rleEncode{})) + addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleEncode{})) + addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 200, rleEncode{}, 100, Nop)) + } + } + for _, tc := range tests { + str, iter, err := doTransform(tc) + mi := tc.wantIter != 0 && tc.wantIter != iter + if str != tc.wantStr || err != tc.wantErr || mi { + t.Errorf("%s:\ngot iter:%d, %q, %v\nwant iter:%d, %q, %v", tc, iter, str, err, tc.wantIter, tc.wantStr, tc.wantErr) + } + break + } +} + +func TestRemoveFunc(t *testing.T) { + filter := RemoveFunc(func(r rune) bool { + return strings.IndexRune("ab\u0300\u1234,", r) != -1 + }) + tests := []testCase{ + { + src: ",", + wantStr: "", + }, + + { + src: "c", + wantStr: "c", + }, + + { + src: "\u2345", + wantStr: "\u2345", + }, + + { + src: "tschüß", + wantStr: "tschüß", + }, + + { + src: ",до,свидания,", + wantStr: "досвидания", + }, + + { + src: "a\xbd\xb2=\xbc ⌘", + wantStr: "\uFFFD\uFFFD=\uFFFD ⌘", + }, + + { + // If we didn't replace illegal bytes with RuneError, the result + // would be \u0300 or the code would need to be more complex. + src: "\xcc\u0300\x80", + wantStr: "\uFFFD\uFFFD", + }, + + { + src: "\xcc\u0300\x80", + dstSize: 3, + wantStr: "\uFFFD\uFFFD", + wantIter: 2, + }, + + { + // Test a long buffer greater than the internal buffer size + src: "hello\xcc\xcc\xccworld", + srcSize: 13, + wantStr: "hello\uFFFD\uFFFD\uFFFDworld", + wantIter: 1, + }, + + { + src: "\u2345", + dstSize: 2, + wantStr: "", + wantErr: ErrShortDst, + }, + + { + src: "\xcc", + dstSize: 2, + wantStr: "", + wantErr: ErrShortDst, + }, + + { + src: "\u0300", + dstSize: 2, + srcSize: 1, + wantStr: "", + wantErr: ErrShortSrc, + }, + + { + t: RemoveFunc(func(r rune) bool { + return r == utf8.RuneError + }), + src: "\xcc\u0300\x80", + wantStr: "\u0300", + }, + } + + for _, tc := range tests { + tc.desc = tc.src + if tc.t == nil { + tc.t = filter + } + if tc.dstSize == 0 { + tc.dstSize = 100 + } + if tc.srcSize == 0 { + tc.srcSize = 100 + } + str, iter, err := doTransform(tc) + mi := tc.wantIter != 0 && tc.wantIter != iter + if str != tc.wantStr || err != tc.wantErr || mi { + t.Errorf("%+q:\ngot iter:%d, %+q, %v\nwant iter:%d, %+q, %v", tc.src, iter, str, err, tc.wantIter, tc.wantStr, tc.wantErr) + } + + tc.src = str + idem, _, _ := doTransform(tc) + if str != idem { + t.Errorf("%+q: found %+q; want %+q", tc.src, idem, str) + } + } +} + +func testString(t *testing.T, f func(Transformer, string) (string, int, error)) { + for _, tt := range append(testCases, chainTests()...) { + if tt.desc == "allowStutter = true" { + // We don't have control over the buffer size, so we eliminate tests + // that depend on a specific buffer size being set. + continue + } + if tt.wantErr == ErrShortDst || tt.wantErr == ErrShortSrc { + // The result string will be different. + continue + } + got, n, err := f(tt.t, tt.src) + if tt.wantErr != err { + t.Errorf("%s:error: got %v; want %v", tt.desc, err, tt.wantErr) + } + if got, want := err == nil, n == len(tt.src); got != want { + t.Errorf("%s:n: got %v; want %v", tt.desc, got, want) + } + if got != tt.wantStr { + t.Errorf("%s:string: got %q; want %q", tt.desc, got, tt.wantStr) + } + } +} + +func TestBytes(t *testing.T) { + testString(t, func(z Transformer, s string) (string, int, error) { + b, n, err := Bytes(z, []byte(s)) + return string(b), n, err + }) +} + +func TestString(t *testing.T) { + testString(t, String) + + // Overrun the internal destination buffer. + for i, s := range []string{ + strings.Repeat("a", initialBufSize-1), + strings.Repeat("a", initialBufSize+0), + strings.Repeat("a", initialBufSize+1), + strings.Repeat("A", initialBufSize-1), + strings.Repeat("A", initialBufSize+0), + strings.Repeat("A", initialBufSize+1), + strings.Repeat("A", 2*initialBufSize-1), + strings.Repeat("A", 2*initialBufSize+0), + strings.Repeat("A", 2*initialBufSize+1), + strings.Repeat("a", initialBufSize-2) + "A", + strings.Repeat("a", initialBufSize-1) + "A", + strings.Repeat("a", initialBufSize+0) + "A", + strings.Repeat("a", initialBufSize+1) + "A", + } { + got, _, _ := String(lowerCaseASCII{}, s) + if want := strings.ToLower(s); got != want { + t.Errorf("%d:dst buffer test: got %s (%d); want %s (%d)", i, got, len(got), want, len(want)) + } + } + + // Overrun the internal source buffer. + for i, s := range []string{ + strings.Repeat("a", initialBufSize-1), + strings.Repeat("a", initialBufSize+0), + strings.Repeat("a", initialBufSize+1), + strings.Repeat("a", 2*initialBufSize+1), + strings.Repeat("a", 2*initialBufSize+0), + strings.Repeat("a", 2*initialBufSize+1), + } { + got, _, _ := String(rleEncode{}, s) + if want := fmt.Sprintf("%da", len(s)); got != want { + t.Errorf("%d:src buffer test: got %s (%d); want %s (%d)", i, got, len(got), want, len(want)) + } + } + + // Test allocations for non-changing strings. + // Note we still need to allocate a single buffer. + for i, s := range []string{ + "", + "123", + "123456789", + strings.Repeat("a", initialBufSize), + strings.Repeat("a", 10*initialBufSize), + } { + if n := testing.AllocsPerRun(5, func() { String(&lowerCaseASCII{}, s) }); n > 1 { + t.Errorf("%d: #allocs was %f; want 1", i, n) + } + } +} + +// TestBytesAllocation tests that buffer growth stays limited with the trickler +// transformer, which behaves oddly but within spec. In case buffer growth is +// not correctly handled, the test will either panic with a failed allocation or +// thrash. To ensure the tests terminate under the last condition, we time out +// after some sufficiently long period of time. +func TestBytesAllocation(t *testing.T) { + done := make(chan bool) + go func() { + in := bytes.Repeat([]byte{'a'}, 1000) + tr := trickler(make([]byte, 1)) + Bytes(&tr, in) + done <- true + }() + select { + case <-done: + case <-time.After(3 * time.Second): + t.Error("time out, likely due to excessive allocation") + } +} + +// TestStringAllocation tests that buffer growth stays limited with the trickler +// transformer, which behaves oddly but within spec. In case buffer growth is +// not correctly handled, the test will either panic with a failed allocation or +// thrash. To ensure the tests terminate under the last condition, we time out +// after some sufficiently long period of time. +func TestStringAllocation(t *testing.T) { + done := make(chan bool) + go func() { + in := strings.Repeat("a", 1000) + tr := trickler(make([]byte, 1)) + String(&tr, in) + done <- true + }() + select { + case <-done: + case <-time.After(3 * time.Second): + t.Error("time out, likely due to excessive allocation") + } +} + +func BenchmarkStringLower(b *testing.B) { + in := strings.Repeat("a", 4096) + for i := 0; i < b.N; i++ { + String(&lowerCaseASCII{}, in) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/composition.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/composition.go new file mode 100644 index 000000000..d17b278ad --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/composition.go @@ -0,0 +1,514 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import "unicode/utf8" + +const ( + maxNonStarters = 30 + // The maximum number of characters needed for a buffer is + // maxNonStarters + 1 for the starter + 1 for the GCJ + maxBufferSize = maxNonStarters + 2 + maxNFCExpansion = 3 // NFC(0x1D160) + maxNFKCExpansion = 18 // NFKC(0xFDFA) + + maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128 +) + +// ssState is used for reporting the segment state after inserting a rune. +// It is returned by streamSafe.next. +type ssState int + +const ( + // Indicates a rune was successfully added to the segment. + ssSuccess ssState = iota + // Indicates a rune starts a new segment and should not be added. + ssStarter + // Indicates a rune caused a segment overflow and a CGJ should be inserted. + ssOverflow +) + +// streamSafe implements the policy of when a CGJ should be inserted. +type streamSafe uint8 + +// mkStreamSafe is a shorthand for declaring a streamSafe var and calling +// first on it. +func mkStreamSafe(p Properties) streamSafe { + return streamSafe(p.nTrailingNonStarters()) +} + +// first inserts the first rune of a segment. +func (ss *streamSafe) first(p Properties) { + if *ss != 0 { + panic("!= 0") + } + *ss = streamSafe(p.nTrailingNonStarters()) +} + +// insert returns a ssState value to indicate whether a rune represented by p +// can be inserted. +func (ss *streamSafe) next(p Properties) ssState { + if *ss > maxNonStarters { + panic("streamSafe was not reset") + } + n := p.nLeadingNonStarters() + if *ss += streamSafe(n); *ss > maxNonStarters { + *ss = 0 + return ssOverflow + } + // The Stream-Safe Text Processing prescribes that the counting can stop + // as soon as a starter is encountered. However, there are some starters, + // like Jamo V and T, that can combine with other runes, leaving their + // successive non-starters appended to the previous, possibly causing an + // overflow. We will therefore consider any rune with a non-zero nLead to + // be a non-starter. Note that it always hold that if nLead > 0 then + // nLead == nTrail. + if n == 0 { + *ss = 0 + return ssStarter + } + return ssSuccess +} + +// backwards is used for checking for overflow and segment starts +// when traversing a string backwards. Users do not need to call first +// for the first rune. The state of the streamSafe retains the count of +// the non-starters loaded. +func (ss *streamSafe) backwards(p Properties) ssState { + if *ss > maxNonStarters { + panic("streamSafe was not reset") + } + c := *ss + streamSafe(p.nTrailingNonStarters()) + if c > maxNonStarters { + return ssOverflow + } + *ss = c + if p.nLeadingNonStarters() == 0 { + return ssStarter + } + return ssSuccess +} + +func (ss streamSafe) isMax() bool { + return ss == maxNonStarters +} + +// GraphemeJoiner is inserted after maxNonStarters non-starter runes. +const GraphemeJoiner = "\u034F" + +// reorderBuffer is used to normalize a single segment. Characters inserted with +// insert are decomposed and reordered based on CCC. The compose method can +// be used to recombine characters. Note that the byte buffer does not hold +// the UTF-8 characters in order. Only the rune array is maintained in sorted +// order. flush writes the resulting segment to a byte array. +type reorderBuffer struct { + rune [maxBufferSize]Properties // Per character info. + byte [maxByteBufferSize]byte // UTF-8 buffer. Referenced by runeInfo.pos. + nbyte uint8 // Number or bytes. + ss streamSafe // For limiting length of non-starter sequence. + nrune int // Number of runeInfos. + f formInfo + + src input + nsrc int + tmpBytes input + + out []byte + flushF func(*reorderBuffer) bool +} + +func (rb *reorderBuffer) init(f Form, src []byte) { + rb.f = *formTable[f] + rb.src.setBytes(src) + rb.nsrc = len(src) + rb.ss = 0 +} + +func (rb *reorderBuffer) initString(f Form, src string) { + rb.f = *formTable[f] + rb.src.setString(src) + rb.nsrc = len(src) + rb.ss = 0 +} + +func (rb *reorderBuffer) setFlusher(out []byte, f func(*reorderBuffer) bool) { + rb.out = out + rb.flushF = f +} + +// reset discards all characters from the buffer. +func (rb *reorderBuffer) reset() { + rb.nrune = 0 + rb.nbyte = 0 + rb.ss = 0 +} + +func (rb *reorderBuffer) doFlush() bool { + if rb.f.composing { + rb.compose() + } + res := rb.flushF(rb) + rb.reset() + return res +} + +// appendFlush appends the normalized segment to rb.out. +func appendFlush(rb *reorderBuffer) bool { + for i := 0; i < rb.nrune; i++ { + start := rb.rune[i].pos + end := start + rb.rune[i].size + rb.out = append(rb.out, rb.byte[start:end]...) + } + return true +} + +// flush appends the normalized segment to out and resets rb. +func (rb *reorderBuffer) flush(out []byte) []byte { + for i := 0; i < rb.nrune; i++ { + start := rb.rune[i].pos + end := start + rb.rune[i].size + out = append(out, rb.byte[start:end]...) + } + rb.reset() + return out +} + +// flushCopy copies the normalized segment to buf and resets rb. +// It returns the number of bytes written to buf. +func (rb *reorderBuffer) flushCopy(buf []byte) int { + p := 0 + for i := 0; i < rb.nrune; i++ { + runep := rb.rune[i] + p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size]) + } + rb.reset() + return p +} + +// insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class. +// It returns false if the buffer is not large enough to hold the rune. +// It is used internally by insert and insertString only. +func (rb *reorderBuffer) insertOrdered(info Properties) { + n := rb.nrune + b := rb.rune[:] + cc := info.ccc + if cc > 0 { + // Find insertion position + move elements to make room. + for ; n > 0; n-- { + if b[n-1].ccc <= cc { + break + } + b[n] = b[n-1] + } + } + rb.nrune += 1 + pos := uint8(rb.nbyte) + rb.nbyte += utf8.UTFMax + info.pos = pos + b[n] = info +} + +// insertErr is an error code returned by insert. Using this type instead +// of error improves performance up to 20% for many of the benchmarks. +type insertErr int + +const ( + iSuccess insertErr = -iota + iShortDst + iShortSrc +) + +// insertFlush inserts the given rune in the buffer ordered by CCC. +// If a decomposition with multiple segments are encountered, they leading +// ones are flushed. +// It returns a non-zero error code if the rune was not inserted. +func (rb *reorderBuffer) insertFlush(src input, i int, info Properties) insertErr { + if rune := src.hangul(i); rune != 0 { + rb.decomposeHangul(rune) + return iSuccess + } + if info.hasDecomposition() { + return rb.insertDecomposed(info.Decomposition()) + } + rb.insertSingle(src, i, info) + return iSuccess +} + +// insertUnsafe inserts the given rune in the buffer ordered by CCC. +// It is assumed there is sufficient space to hold the runes. It is the +// responsibility of the caller to ensure this. This can be done by checking +// the state returned by the streamSafe type. +func (rb *reorderBuffer) insertUnsafe(src input, i int, info Properties) { + if rune := src.hangul(i); rune != 0 { + rb.decomposeHangul(rune) + } + if info.hasDecomposition() { + // TODO: inline. + rb.insertDecomposed(info.Decomposition()) + } else { + rb.insertSingle(src, i, info) + } +} + +// insertDecomposed inserts an entry in to the reorderBuffer for each rune +// in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes. +// It flushes the buffer on each new segment start. +func (rb *reorderBuffer) insertDecomposed(dcomp []byte) insertErr { + rb.tmpBytes.setBytes(dcomp) + for i := 0; i < len(dcomp); { + info := rb.f.info(rb.tmpBytes, i) + if info.BoundaryBefore() && rb.nrune > 0 && !rb.doFlush() { + return iShortDst + } + i += copy(rb.byte[rb.nbyte:], dcomp[i:i+int(info.size)]) + rb.insertOrdered(info) + } + return iSuccess +} + +// insertSingle inserts an entry in the reorderBuffer for the rune at +// position i. info is the runeInfo for the rune at position i. +func (rb *reorderBuffer) insertSingle(src input, i int, info Properties) { + src.copySlice(rb.byte[rb.nbyte:], i, i+int(info.size)) + rb.insertOrdered(info) +} + +// insertCGJ inserts a Combining Grapheme Joiner (0x034f) into rb. +func (rb *reorderBuffer) insertCGJ() { + rb.insertSingle(input{str: GraphemeJoiner}, 0, Properties{size: uint8(len(GraphemeJoiner))}) +} + +// appendRune inserts a rune at the end of the buffer. It is used for Hangul. +func (rb *reorderBuffer) appendRune(r rune) { + bn := rb.nbyte + sz := utf8.EncodeRune(rb.byte[bn:], rune(r)) + rb.nbyte += utf8.UTFMax + rb.rune[rb.nrune] = Properties{pos: bn, size: uint8(sz)} + rb.nrune++ +} + +// assignRune sets a rune at position pos. It is used for Hangul and recomposition. +func (rb *reorderBuffer) assignRune(pos int, r rune) { + bn := rb.rune[pos].pos + sz := utf8.EncodeRune(rb.byte[bn:], rune(r)) + rb.rune[pos] = Properties{pos: bn, size: uint8(sz)} +} + +// runeAt returns the rune at position n. It is used for Hangul and recomposition. +func (rb *reorderBuffer) runeAt(n int) rune { + inf := rb.rune[n] + r, _ := utf8.DecodeRune(rb.byte[inf.pos : inf.pos+inf.size]) + return r +} + +// bytesAt returns the UTF-8 encoding of the rune at position n. +// It is used for Hangul and recomposition. +func (rb *reorderBuffer) bytesAt(n int) []byte { + inf := rb.rune[n] + return rb.byte[inf.pos : int(inf.pos)+int(inf.size)] +} + +// For Hangul we combine algorithmically, instead of using tables. +const ( + hangulBase = 0xAC00 // UTF-8(hangulBase) -> EA B0 80 + hangulBase0 = 0xEA + hangulBase1 = 0xB0 + hangulBase2 = 0x80 + + hangulEnd = hangulBase + jamoLVTCount // UTF-8(0xD7A4) -> ED 9E A4 + hangulEnd0 = 0xED + hangulEnd1 = 0x9E + hangulEnd2 = 0xA4 + + jamoLBase = 0x1100 // UTF-8(jamoLBase) -> E1 84 00 + jamoLBase0 = 0xE1 + jamoLBase1 = 0x84 + jamoLEnd = 0x1113 + jamoVBase = 0x1161 + jamoVEnd = 0x1176 + jamoTBase = 0x11A7 + jamoTEnd = 0x11C3 + + jamoTCount = 28 + jamoVCount = 21 + jamoVTCount = 21 * 28 + jamoLVTCount = 19 * 21 * 28 +) + +const hangulUTF8Size = 3 + +func isHangul(b []byte) bool { + if len(b) < hangulUTF8Size { + return false + } + b0 := b[0] + if b0 < hangulBase0 { + return false + } + b1 := b[1] + switch { + case b0 == hangulBase0: + return b1 >= hangulBase1 + case b0 < hangulEnd0: + return true + case b0 > hangulEnd0: + return false + case b1 < hangulEnd1: + return true + } + return b1 == hangulEnd1 && b[2] < hangulEnd2 +} + +func isHangulString(b string) bool { + if len(b) < hangulUTF8Size { + return false + } + b0 := b[0] + if b0 < hangulBase0 { + return false + } + b1 := b[1] + switch { + case b0 == hangulBase0: + return b1 >= hangulBase1 + case b0 < hangulEnd0: + return true + case b0 > hangulEnd0: + return false + case b1 < hangulEnd1: + return true + } + return b1 == hangulEnd1 && b[2] < hangulEnd2 +} + +// Caller must ensure len(b) >= 2. +func isJamoVT(b []byte) bool { + // True if (rune & 0xff00) == jamoLBase + return b[0] == jamoLBase0 && (b[1]&0xFC) == jamoLBase1 +} + +func isHangulWithoutJamoT(b []byte) bool { + c, _ := utf8.DecodeRune(b) + c -= hangulBase + return c < jamoLVTCount && c%jamoTCount == 0 +} + +// decomposeHangul writes the decomposed Hangul to buf and returns the number +// of bytes written. len(buf) should be at least 9. +func decomposeHangul(buf []byte, r rune) int { + const JamoUTF8Len = 3 + r -= hangulBase + x := r % jamoTCount + r /= jamoTCount + utf8.EncodeRune(buf, jamoLBase+r/jamoVCount) + utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount) + if x != 0 { + utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x) + return 3 * JamoUTF8Len + } + return 2 * JamoUTF8Len +} + +// decomposeHangul algorithmically decomposes a Hangul rune into +// its Jamo components. +// See http://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul. +func (rb *reorderBuffer) decomposeHangul(r rune) { + r -= hangulBase + x := r % jamoTCount + r /= jamoTCount + rb.appendRune(jamoLBase + r/jamoVCount) + rb.appendRune(jamoVBase + r%jamoVCount) + if x != 0 { + rb.appendRune(jamoTBase + x) + } +} + +// combineHangul algorithmically combines Jamo character components into Hangul. +// See http://unicode.org/reports/tr15/#Hangul for details on combining Hangul. +func (rb *reorderBuffer) combineHangul(s, i, k int) { + b := rb.rune[:] + bn := rb.nrune + for ; i < bn; i++ { + cccB := b[k-1].ccc + cccC := b[i].ccc + if cccB == 0 { + s = k - 1 + } + if s != k-1 && cccB >= cccC { + // b[i] is blocked by greater-equal cccX below it + b[k] = b[i] + k++ + } else { + l := rb.runeAt(s) // also used to compare to hangulBase + v := rb.runeAt(i) // also used to compare to jamoT + switch { + case jamoLBase <= l && l < jamoLEnd && + jamoVBase <= v && v < jamoVEnd: + // 11xx plus 116x to LV + rb.assignRune(s, hangulBase+ + (l-jamoLBase)*jamoVTCount+(v-jamoVBase)*jamoTCount) + case hangulBase <= l && l < hangulEnd && + jamoTBase < v && v < jamoTEnd && + ((l-hangulBase)%jamoTCount) == 0: + // ACxx plus 11Ax to LVT + rb.assignRune(s, l+v-jamoTBase) + default: + b[k] = b[i] + k++ + } + } + } + rb.nrune = k +} + +// compose recombines the runes in the buffer. +// It should only be used to recompose a single segment, as it will not +// handle alternations between Hangul and non-Hangul characters correctly. +func (rb *reorderBuffer) compose() { + // UAX #15, section X5 , including Corrigendum #5 + // "In any character sequence beginning with starter S, a character C is + // blocked from S if and only if there is some character B between S + // and C, and either B is a starter or it has the same or higher + // combining class as C." + bn := rb.nrune + if bn == 0 { + return + } + k := 1 + b := rb.rune[:] + for s, i := 0, 1; i < bn; i++ { + if isJamoVT(rb.bytesAt(i)) { + // Redo from start in Hangul mode. Necessary to support + // U+320E..U+321E in NFKC mode. + rb.combineHangul(s, i, k) + return + } + ii := b[i] + // We can only use combineForward as a filter if we later + // get the info for the combined character. This is more + // expensive than using the filter. Using combinesBackward() + // is safe. + if ii.combinesBackward() { + cccB := b[k-1].ccc + cccC := ii.ccc + blocked := false // b[i] blocked by starter or greater or equal CCC? + if cccB == 0 { + s = k - 1 + } else { + blocked = s != k-1 && cccB >= cccC + } + if !blocked { + combined := combine(rb.runeAt(s), rb.runeAt(i)) + if combined != 0 { + rb.assignRune(s, combined) + continue + } + } + } + b[k] = b[i] + k++ + } + rb.nrune = k +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/composition_test.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/composition_test.go new file mode 100644 index 000000000..11684069d --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/composition_test.go @@ -0,0 +1,130 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import "testing" + +// TestCase is used for most tests. +type TestCase struct { + in []rune + out []rune +} + +func runTests(t *testing.T, name string, fm Form, tests []TestCase) { + rb := reorderBuffer{} + rb.init(fm, nil) + for i, test := range tests { + rb.setFlusher(nil, appendFlush) + for j, rune := range test.in { + b := []byte(string(rune)) + src := inputBytes(b) + info := rb.f.info(src, 0) + if j == 0 { + rb.ss.first(info) + } else { + rb.ss.next(info) + } + if rb.insertFlush(src, 0, info) < 0 { + t.Errorf("%s:%d: insert failed for rune %d", name, i, j) + } + } + rb.doFlush() + was := string(rb.out) + want := string(test.out) + if len(was) != len(want) { + t.Errorf("%s:%d: length = %d; want %d", name, i, len(was), len(want)) + } + if was != want { + k, pfx := pidx(was, want) + t.Errorf("%s:%d: \nwas %s%+q; \nwant %s%+q", name, i, pfx, was[k:], pfx, want[k:]) + } + } +} + +func TestFlush(t *testing.T) { + const ( + hello = "Hello " + world = "world!" + ) + buf := make([]byte, maxByteBufferSize) + p := copy(buf, hello) + out := buf[p:] + rb := reorderBuffer{} + rb.initString(NFC, world) + if i := rb.flushCopy(out); i != 0 { + t.Errorf("wrote bytes on flush of empty buffer. (len(out) = %d)", i) + } + + for i := range world { + // No need to set streamSafe values for this test. + rb.insertFlush(rb.src, i, rb.f.info(rb.src, i)) + n := rb.flushCopy(out) + out = out[n:] + p += n + } + + was := buf[:p] + want := hello + world + if string(was) != want { + t.Errorf(`output after flush was "%s"; want "%s"`, string(was), want) + } + if rb.nrune != 0 { + t.Errorf("non-null size of info buffer (rb.nrune == %d)", rb.nrune) + } + if rb.nbyte != 0 { + t.Errorf("non-null size of byte buffer (rb.nbyte == %d)", rb.nbyte) + } +} + +var insertTests = []TestCase{ + {[]rune{'a'}, []rune{'a'}}, + {[]rune{0x300}, []rune{0x300}}, + {[]rune{0x300, 0x316}, []rune{0x316, 0x300}}, // CCC(0x300)==230; CCC(0x316)==220 + {[]rune{0x316, 0x300}, []rune{0x316, 0x300}}, + {[]rune{0x41, 0x316, 0x300}, []rune{0x41, 0x316, 0x300}}, + {[]rune{0x41, 0x300, 0x316}, []rune{0x41, 0x316, 0x300}}, + {[]rune{0x300, 0x316, 0x41}, []rune{0x316, 0x300, 0x41}}, + {[]rune{0x41, 0x300, 0x40, 0x316}, []rune{0x41, 0x300, 0x40, 0x316}}, +} + +func TestInsert(t *testing.T) { + runTests(t, "TestInsert", NFD, insertTests) +} + +var decompositionNFDTest = []TestCase{ + {[]rune{0xC0}, []rune{0x41, 0x300}}, + {[]rune{0xAC00}, []rune{0x1100, 0x1161}}, + {[]rune{0x01C4}, []rune{0x01C4}}, + {[]rune{0x320E}, []rune{0x320E}}, + {[]rune("음ẻ과"), []rune{0x110B, 0x1173, 0x11B7, 0x65, 0x309, 0x1100, 0x116A}}, +} + +var decompositionNFKDTest = []TestCase{ + {[]rune{0xC0}, []rune{0x41, 0x300}}, + {[]rune{0xAC00}, []rune{0x1100, 0x1161}}, + {[]rune{0x01C4}, []rune{0x44, 0x5A, 0x030C}}, + {[]rune{0x320E}, []rune{0x28, 0x1100, 0x1161, 0x29}}, +} + +func TestDecomposition(t *testing.T) { + runTests(t, "TestDecompositionNFD", NFD, decompositionNFDTest) + runTests(t, "TestDecompositionNFKD", NFKD, decompositionNFKDTest) +} + +var compositionTest = []TestCase{ + {[]rune{0x41, 0x300}, []rune{0xC0}}, + {[]rune{0x41, 0x316}, []rune{0x41, 0x316}}, + {[]rune{0x41, 0x300, 0x35D}, []rune{0xC0, 0x35D}}, + {[]rune{0x41, 0x316, 0x300}, []rune{0xC0, 0x316}}, + // blocking starter + {[]rune{0x41, 0x316, 0x40, 0x300}, []rune{0x41, 0x316, 0x40, 0x300}}, + {[]rune{0x1100, 0x1161}, []rune{0xAC00}}, + // parenthesized Hangul, alternate between ASCII and Hangul. + {[]rune{0x28, 0x1100, 0x1161, 0x29}, []rune{0x28, 0xAC00, 0x29}}, +} + +func TestComposition(t *testing.T) { + runTests(t, "TestComposition", NFC, compositionTest) +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/example_iter_test.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/example_iter_test.go new file mode 100644 index 000000000..82df89c7b --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/example_iter_test.go @@ -0,0 +1,82 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm_test + +import ( + "bytes" + "fmt" + "unicode/utf8" + + "golang.org/x/text/unicode/norm" +) + +// EqualSimple uses a norm.Iter to compare two non-normalized +// strings for equivalence. +func EqualSimple(a, b string) bool { + var ia, ib norm.Iter + ia.InitString(norm.NFKD, a) + ib.InitString(norm.NFKD, b) + for !ia.Done() && !ib.Done() { + if !bytes.Equal(ia.Next(), ib.Next()) { + return false + } + } + return ia.Done() && ib.Done() +} + +// FindPrefix finds the longest common prefix of ASCII characters +// of a and b. +func FindPrefix(a, b string) int { + i := 0 + for ; i < len(a) && i < len(b) && a[i] < utf8.RuneSelf && a[i] == b[i]; i++ { + } + return i +} + +// EqualOpt is like EqualSimple, but optimizes the special +// case for ASCII characters. +func EqualOpt(a, b string) bool { + n := FindPrefix(a, b) + a, b = a[n:], b[n:] + var ia, ib norm.Iter + ia.InitString(norm.NFKD, a) + ib.InitString(norm.NFKD, b) + for !ia.Done() && !ib.Done() { + if !bytes.Equal(ia.Next(), ib.Next()) { + return false + } + if n := int64(FindPrefix(a[ia.Pos():], b[ib.Pos():])); n != 0 { + ia.Seek(n, 1) + ib.Seek(n, 1) + } + } + return ia.Done() && ib.Done() +} + +var compareTests = []struct{ a, b string }{ + {"aaa", "aaa"}, + {"aaa", "aab"}, + {"a\u0300a", "\u00E0a"}, + {"a\u0300\u0320b", "a\u0320\u0300b"}, + {"\u1E0A\u0323", "\x44\u0323\u0307"}, + // A character that decomposes into multiple segments + // spans several iterations. + {"\u3304", "\u30A4\u30CB\u30F3\u30AF\u3099"}, +} + +func ExampleIter() { + for i, t := range compareTests { + r0 := EqualSimple(t.a, t.b) + r1 := EqualOpt(t.a, t.b) + fmt.Printf("%d: %v %v\n", i, r0, r1) + } + // Output: + // 0: true true + // 1: false false + // 2: true true + // 3: true true + // 4: true true + // 5: true true +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/forminfo.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/forminfo.go new file mode 100644 index 000000000..15a67c653 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/forminfo.go @@ -0,0 +1,256 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +// This file contains Form-specific logic and wrappers for data in tables.go. + +// Rune info is stored in a separate trie per composing form. A composing form +// and its corresponding decomposing form share the same trie. Each trie maps +// a rune to a uint16. The values take two forms. For v >= 0x8000: +// bits +// 15: 1 (inverse of NFD_QD bit of qcInfo) +// 13..7: qcInfo (see below). isYesD is always true (no decompostion). +// 6..0: ccc (compressed CCC value). +// For v < 0x8000, the respective rune has a decomposition and v is an index +// into a byte array of UTF-8 decomposition sequences and additional info and +// has the form: +//
* [ []] +// The header contains the number of bytes in the decomposition (excluding this +// length byte). The two most significant bits of this length byte correspond +// to bit 5 and 4 of qcInfo (see below). The byte sequence itself starts at v+1. +// The byte sequence is followed by a trailing and leading CCC if the values +// for these are not zero. The value of v determines which ccc are appended +// to the sequences. For v < firstCCC, there are none, for v >= firstCCC, +// the sequence is followed by a trailing ccc, and for v >= firstLeadingCC +// there is an additional leading ccc. The value of tccc itself is the +// trailing CCC shifted left 2 bits. The two least-significant bits of tccc +// are the number of trailing non-starters. + +const ( + qcInfoMask = 0x3F // to clear all but the relevant bits in a qcInfo + headerLenMask = 0x3F // extract the length value from the header byte + headerFlagsMask = 0xC0 // extract the qcInfo bits from the header byte +) + +// Properties provides access to normalization properties of a rune. +type Properties struct { + pos uint8 // start position in reorderBuffer; used in composition.go + size uint8 // length of UTF-8 encoding of this rune + ccc uint8 // leading canonical combining class (ccc if not decomposition) + tccc uint8 // trailing canonical combining class (ccc if not decomposition) + nLead uint8 // number of leading non-starters. + flags qcInfo // quick check flags + index uint16 +} + +// functions dispatchable per form +type lookupFunc func(b input, i int) Properties + +// formInfo holds Form-specific functions and tables. +type formInfo struct { + form Form + composing, compatibility bool // form type + info lookupFunc + nextMain iterFunc +} + +var formTable []*formInfo + +func init() { + formTable = make([]*formInfo, 4) + + for i := range formTable { + f := &formInfo{} + formTable[i] = f + f.form = Form(i) + if Form(i) == NFKD || Form(i) == NFKC { + f.compatibility = true + f.info = lookupInfoNFKC + } else { + f.info = lookupInfoNFC + } + f.nextMain = nextDecomposed + if Form(i) == NFC || Form(i) == NFKC { + f.nextMain = nextComposed + f.composing = true + } + } +} + +// We do not distinguish between boundaries for NFC, NFD, etc. to avoid +// unexpected behavior for the user. For example, in NFD, there is a boundary +// after 'a'. However, 'a' might combine with modifiers, so from the application's +// perspective it is not a good boundary. We will therefore always use the +// boundaries for the combining variants. + +// BoundaryBefore returns true if this rune starts a new segment and +// cannot combine with any rune on the left. +func (p Properties) BoundaryBefore() bool { + if p.ccc == 0 && !p.combinesBackward() { + return true + } + // We assume that the CCC of the first character in a decomposition + // is always non-zero if different from info.ccc and that we can return + // false at this point. This is verified by maketables. + return false +} + +// BoundaryAfter returns true if runes cannot combine with or otherwise +// interact with this or previous runes. +func (p Properties) BoundaryAfter() bool { + // TODO: loosen these conditions. + return p.isInert() +} + +// We pack quick check data in 4 bits: +// 5: Combines forward (0 == false, 1 == true) +// 4..3: NFC_QC Yes(00), No (10), or Maybe (11) +// 2: NFD_QC Yes (0) or No (1). No also means there is a decomposition. +// 1..0: Number of trailing non-starters. +// +// When all 4 bits are zero, the character is inert, meaning it is never +// influenced by normalization. +type qcInfo uint8 + +func (p Properties) isYesC() bool { return p.flags&0x10 == 0 } +func (p Properties) isYesD() bool { return p.flags&0x4 == 0 } + +func (p Properties) combinesForward() bool { return p.flags&0x20 != 0 } +func (p Properties) combinesBackward() bool { return p.flags&0x8 != 0 } // == isMaybe +func (p Properties) hasDecomposition() bool { return p.flags&0x4 != 0 } // == isNoD + +func (p Properties) isInert() bool { + return p.flags&qcInfoMask == 0 && p.ccc == 0 +} + +func (p Properties) multiSegment() bool { + return p.index >= firstMulti && p.index < endMulti +} + +func (p Properties) nLeadingNonStarters() uint8 { + return p.nLead +} + +func (p Properties) nTrailingNonStarters() uint8 { + return uint8(p.flags & 0x03) +} + +// Decomposition returns the decomposition for the underlying rune +// or nil if there is none. +func (p Properties) Decomposition() []byte { + // TODO: create the decomposition for Hangul? + if p.index == 0 { + return nil + } + i := p.index + n := decomps[i] & headerLenMask + i++ + return decomps[i : i+uint16(n)] +} + +// Size returns the length of UTF-8 encoding of the rune. +func (p Properties) Size() int { + return int(p.size) +} + +// CCC returns the canonical combining class of the underlying rune. +func (p Properties) CCC() uint8 { + if p.index >= firstCCCZeroExcept { + return 0 + } + return ccc[p.ccc] +} + +// LeadCCC returns the CCC of the first rune in the decomposition. +// If there is no decomposition, LeadCCC equals CCC. +func (p Properties) LeadCCC() uint8 { + return ccc[p.ccc] +} + +// TrailCCC returns the CCC of the last rune in the decomposition. +// If there is no decomposition, TrailCCC equals CCC. +func (p Properties) TrailCCC() uint8 { + return ccc[p.tccc] +} + +// Recomposition +// We use 32-bit keys instead of 64-bit for the two codepoint keys. +// This clips off the bits of three entries, but we know this will not +// result in a collision. In the unlikely event that changes to +// UnicodeData.txt introduce collisions, the compiler will catch it. +// Note that the recomposition map for NFC and NFKC are identical. + +// combine returns the combined rune or 0 if it doesn't exist. +func combine(a, b rune) rune { + key := uint32(uint16(a))<<16 + uint32(uint16(b)) + return recompMap[key] +} + +func lookupInfoNFC(b input, i int) Properties { + v, sz := b.charinfoNFC(i) + return compInfo(v, sz) +} + +func lookupInfoNFKC(b input, i int) Properties { + v, sz := b.charinfoNFKC(i) + return compInfo(v, sz) +} + +// Properties returns properties for the first rune in s. +func (f Form) Properties(s []byte) Properties { + if f == NFC || f == NFD { + return compInfo(nfcData.lookup(s)) + } + return compInfo(nfkcData.lookup(s)) +} + +// PropertiesString returns properties for the first rune in s. +func (f Form) PropertiesString(s string) Properties { + if f == NFC || f == NFD { + return compInfo(nfcData.lookupString(s)) + } + return compInfo(nfkcData.lookupString(s)) +} + +// compInfo converts the information contained in v and sz +// to a Properties. See the comment at the top of the file +// for more information on the format. +func compInfo(v uint16, sz int) Properties { + if v == 0 { + return Properties{size: uint8(sz)} + } else if v >= 0x8000 { + p := Properties{ + size: uint8(sz), + ccc: uint8(v), + tccc: uint8(v), + flags: qcInfo(v >> 8), + } + if p.ccc > 0 || p.combinesBackward() { + p.nLead = uint8(p.flags & 0x3) + } + return p + } + // has decomposition + h := decomps[v] + f := (qcInfo(h&headerFlagsMask) >> 2) | 0x4 + p := Properties{size: uint8(sz), flags: f, index: v} + if v >= firstCCC { + v += uint16(h&headerLenMask) + 1 + c := decomps[v] + p.tccc = c >> 2 + p.flags |= qcInfo(c & 0x3) + if v >= firstLeadingCCC { + p.nLead = c & 0x3 + if v >= firstStarterWithNLead { + // We were tricked. Remove the decomposition. + p.flags &= 0x03 + p.index = 0 + return p + } + p.ccc = decomps[v+1] + } + } + return p +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/forminfo_test.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/forminfo_test.go new file mode 100644 index 000000000..e15ba9bee --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/forminfo_test.go @@ -0,0 +1,54 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build test + +package norm + +import "testing" + +func TestProperties(t *testing.T) { + var d runeData + CK := [2]string{"C", "K"} + for k, r := 1, rune(0); r < 0x2ffff; r++ { + if k < len(testData) && r == testData[k].r { + d = testData[k] + k++ + } + s := string(r) + for j, p := range []Properties{NFC.PropertiesString(s), NFKC.PropertiesString(s)} { + f := d.f[j] + if p.CCC() != d.ccc { + t.Errorf("%U: ccc(%s): was %d; want %d %X", r, CK[j], p.CCC(), d.ccc, p.index) + } + if p.isYesC() != (f.qc == Yes) { + t.Errorf("%U: YesC(%s): was %v; want %v", r, CK[j], p.isYesC(), f.qc == Yes) + } + if p.combinesBackward() != (f.qc == Maybe) { + t.Errorf("%U: combines backwards(%s): was %v; want %v", r, CK[j], p.combinesBackward(), f.qc == Maybe) + } + if p.nLeadingNonStarters() != d.nLead { + t.Errorf("%U: nLead(%s): was %d; want %d %#v %#v", r, CK[j], p.nLeadingNonStarters(), d.nLead, p, d) + } + if p.nTrailingNonStarters() != d.nTrail { + t.Errorf("%U: nTrail(%s): was %d; want %d %#v %#v", r, CK[j], p.nTrailingNonStarters(), d.nTrail, p, d) + } + if p.combinesForward() != f.combinesForward { + t.Errorf("%U: combines forward(%s): was %v; want %v %#v", r, CK[j], p.combinesForward(), f.combinesForward, p) + } + // Skip Hangul as it is algorithmically computed. + if r >= hangulBase && r < hangulEnd { + continue + } + if p.hasDecomposition() { + if has := f.decomposition != ""; !has { + t.Errorf("%U: hasDecomposition(%s): was %v; want %v", r, CK[j], p.hasDecomposition(), has) + } + if string(p.Decomposition()) != f.decomposition { + t.Errorf("%U: decomp(%s): was %+q; want %+q", r, CK[j], p.Decomposition(), f.decomposition) + } + } + } + } +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/input.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/input.go new file mode 100644 index 000000000..045d4ccce --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/input.go @@ -0,0 +1,105 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import "unicode/utf8" + +type input struct { + str string + bytes []byte +} + +func inputBytes(str []byte) input { + return input{bytes: str} +} + +func inputString(str string) input { + return input{str: str} +} + +func (in *input) setBytes(str []byte) { + in.str = "" + in.bytes = str +} + +func (in *input) setString(str string) { + in.str = str + in.bytes = nil +} + +func (in *input) _byte(p int) byte { + if in.bytes == nil { + return in.str[p] + } + return in.bytes[p] +} + +func (in *input) skipASCII(p, max int) int { + if in.bytes == nil { + for ; p < max && in.str[p] < utf8.RuneSelf; p++ { + } + } else { + for ; p < max && in.bytes[p] < utf8.RuneSelf; p++ { + } + } + return p +} + +func (in *input) skipContinuationBytes(p int) int { + if in.bytes == nil { + for ; p < len(in.str) && !utf8.RuneStart(in.str[p]); p++ { + } + } else { + for ; p < len(in.bytes) && !utf8.RuneStart(in.bytes[p]); p++ { + } + } + return p +} + +func (in *input) appendSlice(buf []byte, b, e int) []byte { + if in.bytes != nil { + return append(buf, in.bytes[b:e]...) + } + for i := b; i < e; i++ { + buf = append(buf, in.str[i]) + } + return buf +} + +func (in *input) copySlice(buf []byte, b, e int) int { + if in.bytes == nil { + return copy(buf, in.str[b:e]) + } + return copy(buf, in.bytes[b:e]) +} + +func (in *input) charinfoNFC(p int) (uint16, int) { + if in.bytes == nil { + return nfcData.lookupString(in.str[p:]) + } + return nfcData.lookup(in.bytes[p:]) +} + +func (in *input) charinfoNFKC(p int) (uint16, int) { + if in.bytes == nil { + return nfkcData.lookupString(in.str[p:]) + } + return nfkcData.lookup(in.bytes[p:]) +} + +func (in *input) hangul(p int) (r rune) { + if in.bytes == nil { + if !isHangulString(in.str[p:]) { + return 0 + } + r, _ = utf8.DecodeRuneInString(in.str[p:]) + } else { + if !isHangul(in.bytes[p:]) { + return 0 + } + r, _ = utf8.DecodeRune(in.bytes[p:]) + } + return r +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/iter.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/iter.go new file mode 100644 index 000000000..0a42a72de --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/iter.go @@ -0,0 +1,450 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "fmt" + "unicode/utf8" +) + +// MaxSegmentSize is the maximum size of a byte buffer needed to consider any +// sequence of starter and non-starter runes for the purpose of normalization. +const MaxSegmentSize = maxByteBufferSize + +// An Iter iterates over a string or byte slice, while normalizing it +// to a given Form. +type Iter struct { + rb reorderBuffer + buf [maxByteBufferSize]byte + info Properties // first character saved from previous iteration + next iterFunc // implementation of next depends on form + asciiF iterFunc + + p int // current position in input source + multiSeg []byte // remainder of multi-segment decomposition +} + +type iterFunc func(*Iter) []byte + +// Init initializes i to iterate over src after normalizing it to Form f. +func (i *Iter) Init(f Form, src []byte) { + i.p = 0 + if len(src) == 0 { + i.setDone() + i.rb.nsrc = 0 + return + } + i.multiSeg = nil + i.rb.init(f, src) + i.next = i.rb.f.nextMain + i.asciiF = nextASCIIBytes + i.info = i.rb.f.info(i.rb.src, i.p) +} + +// InitString initializes i to iterate over src after normalizing it to Form f. +func (i *Iter) InitString(f Form, src string) { + i.p = 0 + if len(src) == 0 { + i.setDone() + i.rb.nsrc = 0 + return + } + i.multiSeg = nil + i.rb.initString(f, src) + i.next = i.rb.f.nextMain + i.asciiF = nextASCIIString + i.info = i.rb.f.info(i.rb.src, i.p) +} + +// Seek sets the segment to be returned by the next call to Next to start +// at position p. It is the responsibility of the caller to set p to the +// start of a UTF8 rune. +func (i *Iter) Seek(offset int64, whence int) (int64, error) { + var abs int64 + switch whence { + case 0: + abs = offset + case 1: + abs = int64(i.p) + offset + case 2: + abs = int64(i.rb.nsrc) + offset + default: + return 0, fmt.Errorf("norm: invalid whence") + } + if abs < 0 { + return 0, fmt.Errorf("norm: negative position") + } + if int(abs) >= i.rb.nsrc { + i.setDone() + return int64(i.p), nil + } + i.p = int(abs) + i.multiSeg = nil + i.next = i.rb.f.nextMain + i.info = i.rb.f.info(i.rb.src, i.p) + return abs, nil +} + +// returnSlice returns a slice of the underlying input type as a byte slice. +// If the underlying is of type []byte, it will simply return a slice. +// If the underlying is of type string, it will copy the slice to the buffer +// and return that. +func (i *Iter) returnSlice(a, b int) []byte { + if i.rb.src.bytes == nil { + return i.buf[:copy(i.buf[:], i.rb.src.str[a:b])] + } + return i.rb.src.bytes[a:b] +} + +// Pos returns the byte position at which the next call to Next will commence processing. +func (i *Iter) Pos() int { + return i.p +} + +func (i *Iter) setDone() { + i.next = nextDone + i.p = i.rb.nsrc +} + +// Done returns true if there is no more input to process. +func (i *Iter) Done() bool { + return i.p >= i.rb.nsrc +} + +// Next returns f(i.input[i.Pos():n]), where n is a boundary of i.input. +// For any input a and b for which f(a) == f(b), subsequent calls +// to Next will return the same segments. +// Modifying runes are grouped together with the preceding starter, if such a starter exists. +// Although not guaranteed, n will typically be the smallest possible n. +func (i *Iter) Next() []byte { + return i.next(i) +} + +func nextASCIIBytes(i *Iter) []byte { + p := i.p + 1 + if p >= i.rb.nsrc { + i.setDone() + return i.rb.src.bytes[i.p:p] + } + if i.rb.src.bytes[p] < utf8.RuneSelf { + p0 := i.p + i.p = p + return i.rb.src.bytes[p0:p] + } + i.info = i.rb.f.info(i.rb.src, i.p) + i.next = i.rb.f.nextMain + return i.next(i) +} + +func nextASCIIString(i *Iter) []byte { + p := i.p + 1 + if p >= i.rb.nsrc { + i.buf[0] = i.rb.src.str[i.p] + i.setDone() + return i.buf[:1] + } + if i.rb.src.str[p] < utf8.RuneSelf { + i.buf[0] = i.rb.src.str[i.p] + i.p = p + return i.buf[:1] + } + i.info = i.rb.f.info(i.rb.src, i.p) + i.next = i.rb.f.nextMain + return i.next(i) +} + +func nextHangul(i *Iter) []byte { + p := i.p + next := p + hangulUTF8Size + if next >= i.rb.nsrc { + i.setDone() + } else if i.rb.src.hangul(next) == 0 { + i.info = i.rb.f.info(i.rb.src, i.p) + i.next = i.rb.f.nextMain + return i.next(i) + } + i.p = next + return i.buf[:decomposeHangul(i.buf[:], i.rb.src.hangul(p))] +} + +func nextDone(i *Iter) []byte { + return nil +} + +// nextMulti is used for iterating over multi-segment decompositions +// for decomposing normal forms. +func nextMulti(i *Iter) []byte { + j := 0 + d := i.multiSeg + // skip first rune + for j = 1; j < len(d) && !utf8.RuneStart(d[j]); j++ { + } + for j < len(d) { + info := i.rb.f.info(input{bytes: d}, j) + if info.BoundaryBefore() { + i.multiSeg = d[j:] + return d[:j] + } + j += int(info.size) + } + // treat last segment as normal decomposition + i.next = i.rb.f.nextMain + return i.next(i) +} + +// nextMultiNorm is used for iterating over multi-segment decompositions +// for composing normal forms. +func nextMultiNorm(i *Iter) []byte { + j := 0 + d := i.multiSeg + for j < len(d) { + info := i.rb.f.info(input{bytes: d}, j) + if info.BoundaryBefore() { + i.rb.compose() + seg := i.buf[:i.rb.flushCopy(i.buf[:])] + i.rb.ss.first(info) + i.rb.insertUnsafe(input{bytes: d}, j, info) + i.multiSeg = d[j+int(info.size):] + return seg + } + i.rb.ss.next(info) + i.rb.insertUnsafe(input{bytes: d}, j, info) + j += int(info.size) + } + i.multiSeg = nil + i.next = nextComposed + return doNormComposed(i) +} + +// nextDecomposed is the implementation of Next for forms NFD and NFKD. +func nextDecomposed(i *Iter) (next []byte) { + outp := 0 + inCopyStart, outCopyStart := i.p, 0 + ss := mkStreamSafe(i.info) + for { + if sz := int(i.info.size); sz <= 1 { + p := i.p + i.p++ // ASCII or illegal byte. Either way, advance by 1. + if i.p >= i.rb.nsrc { + i.setDone() + return i.returnSlice(p, i.p) + } else if i.rb.src._byte(i.p) < utf8.RuneSelf { + i.next = i.asciiF + return i.returnSlice(p, i.p) + } + outp++ + } else if d := i.info.Decomposition(); d != nil { + // Note: If leading CCC != 0, then len(d) == 2 and last is also non-zero. + // Case 1: there is a leftover to copy. In this case the decomposition + // must begin with a modifier and should always be appended. + // Case 2: no leftover. Simply return d if followed by a ccc == 0 value. + p := outp + len(d) + if outp > 0 { + i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p) + if p > len(i.buf) { + return i.buf[:outp] + } + } else if i.info.multiSegment() { + // outp must be 0 as multi-segment decompositions always + // start a new segment. + if i.multiSeg == nil { + i.multiSeg = d + i.next = nextMulti + return nextMulti(i) + } + // We are in the last segment. Treat as normal decomposition. + d = i.multiSeg + i.multiSeg = nil + p = len(d) + } + prevCC := i.info.tccc + if i.p += sz; i.p >= i.rb.nsrc { + i.setDone() + i.info = Properties{} // Force BoundaryBefore to succeed. + } else { + i.info = i.rb.f.info(i.rb.src, i.p) + } + switch ss.next(i.info) { + case ssOverflow: + i.next = nextCGJDecompose + fallthrough + case ssStarter: + if outp > 0 { + copy(i.buf[outp:], d) + return i.buf[:p] + } + return d + } + copy(i.buf[outp:], d) + outp = p + inCopyStart, outCopyStart = i.p, outp + if i.info.ccc < prevCC { + goto doNorm + } + continue + } else if r := i.rb.src.hangul(i.p); r != 0 { + outp = decomposeHangul(i.buf[:], r) + i.p += hangulUTF8Size + inCopyStart, outCopyStart = i.p, outp + if i.p >= i.rb.nsrc { + i.setDone() + break + } else if i.rb.src.hangul(i.p) != 0 { + i.next = nextHangul + return i.buf[:outp] + } + } else { + p := outp + sz + if p > len(i.buf) { + break + } + outp = p + i.p += sz + } + if i.p >= i.rb.nsrc { + i.setDone() + break + } + prevCC := i.info.tccc + i.info = i.rb.f.info(i.rb.src, i.p) + if v := ss.next(i.info); v == ssStarter { + break + } else if v == ssOverflow { + i.next = nextCGJDecompose + break + } + if i.info.ccc < prevCC { + goto doNorm + } + } + if outCopyStart == 0 { + return i.returnSlice(inCopyStart, i.p) + } else if inCopyStart < i.p { + i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p) + } + return i.buf[:outp] +doNorm: + // Insert what we have decomposed so far in the reorderBuffer. + // As we will only reorder, there will always be enough room. + i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p) + i.rb.insertDecomposed(i.buf[0:outp]) + return doNormDecomposed(i) +} + +func doNormDecomposed(i *Iter) []byte { + for { + if s := i.rb.ss.next(i.info); s == ssOverflow { + i.next = nextCGJDecompose + break + } + i.rb.insertUnsafe(i.rb.src, i.p, i.info) + if i.p += int(i.info.size); i.p >= i.rb.nsrc { + i.setDone() + break + } + i.info = i.rb.f.info(i.rb.src, i.p) + if i.info.ccc == 0 { + break + } + } + // new segment or too many combining characters: exit normalization + return i.buf[:i.rb.flushCopy(i.buf[:])] +} + +func nextCGJDecompose(i *Iter) []byte { + i.rb.ss = 0 + i.rb.insertCGJ() + i.next = nextDecomposed + buf := doNormDecomposed(i) + return buf +} + +// nextComposed is the implementation of Next for forms NFC and NFKC. +func nextComposed(i *Iter) []byte { + outp, startp := 0, i.p + var prevCC uint8 + ss := mkStreamSafe(i.info) + for { + if !i.info.isYesC() { + goto doNorm + } + prevCC = i.info.tccc + sz := int(i.info.size) + if sz == 0 { + sz = 1 // illegal rune: copy byte-by-byte + } + p := outp + sz + if p > len(i.buf) { + break + } + outp = p + i.p += sz + if i.p >= i.rb.nsrc { + i.setDone() + break + } else if i.rb.src._byte(i.p) < utf8.RuneSelf { + i.next = i.asciiF + break + } + i.info = i.rb.f.info(i.rb.src, i.p) + if v := ss.next(i.info); v == ssStarter { + break + } else if v == ssOverflow { + i.next = nextCGJCompose + break + } + if i.info.ccc < prevCC { + goto doNorm + } + } + return i.returnSlice(startp, i.p) +doNorm: + i.p = startp + i.info = i.rb.f.info(i.rb.src, i.p) + if i.info.multiSegment() { + d := i.info.Decomposition() + info := i.rb.f.info(input{bytes: d}, 0) + i.rb.insertUnsafe(input{bytes: d}, 0, info) + i.multiSeg = d[int(info.size):] + i.next = nextMultiNorm + return nextMultiNorm(i) + } + i.rb.ss.first(i.info) + i.rb.insertUnsafe(i.rb.src, i.p, i.info) + return doNormComposed(i) +} + +func doNormComposed(i *Iter) []byte { + // First rune should already be inserted. + for { + if i.p += int(i.info.size); i.p >= i.rb.nsrc { + i.setDone() + break + } + i.info = i.rb.f.info(i.rb.src, i.p) + if s := i.rb.ss.next(i.info); s == ssStarter { + break + } else if s == ssOverflow { + i.next = nextCGJCompose + break + } + i.rb.insertUnsafe(i.rb.src, i.p, i.info) + } + i.rb.compose() + seg := i.buf[:i.rb.flushCopy(i.buf[:])] + return seg +} + +func nextCGJCompose(i *Iter) []byte { + i.rb.ss = 0 // instead of first + i.rb.insertCGJ() + i.next = nextComposed + // Note that we treat any rune with nLeadingNonStarters > 0 as a non-starter, + // even if they are not. This is particularly dubious for U+FF9E and UFF9A. + // If we ever change that, insert a check here. + i.rb.ss.first(i.info) + i.rb.insertUnsafe(i.rb.src, i.p, i.info) + return doNormComposed(i) +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/iter_test.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/iter_test.go new file mode 100644 index 000000000..e2aa6f251 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/iter_test.go @@ -0,0 +1,98 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "strings" + "testing" +) + +func doIterNorm(f Form, s string) []byte { + acc := []byte{} + i := Iter{} + i.InitString(f, s) + for !i.Done() { + acc = append(acc, i.Next()...) + } + return acc +} + +func TestIterNext(t *testing.T) { + runNormTests(t, "IterNext", func(f Form, out []byte, s string) []byte { + return doIterNorm(f, string(append(out, s...))) + }) +} + +type SegmentTest struct { + in string + out []string +} + +var segmentTests = []SegmentTest{ + {"\u1E0A\u0323a", []string{"\x44\u0323\u0307", "a", ""}}, + {rep('a', segSize), append(strings.Split(rep('a', segSize), ""), "")}, + {rep('a', segSize+2), append(strings.Split(rep('a', segSize+2), ""), "")}, + {rep('a', segSize) + "\u0300aa", + append(strings.Split(rep('a', segSize-1), ""), "a\u0300", "a", "a", "")}, + + // U+0f73 is NOT treated as a starter as it is a modifier + {"a" + grave(29) + "\u0f73", []string{"a" + grave(29), cgj + "\u0f73"}}, + {"a\u0f73", []string{"a\u0f73"}}, + + // U+ff9e is treated as a non-starter. + // TODO: should we? Note that this will only affect iteration, as whether + // or not we do so does not affect the normalization output and will either + // way result in consistent iteration output. + {"a" + grave(30) + "\uff9e", []string{"a" + grave(30), cgj + "\uff9e"}}, + {"a\uff9e", []string{"a\uff9e"}}, +} + +var segmentTestsK = []SegmentTest{ + {"\u3332", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u3099", ""}}, + // last segment of multi-segment decomposition needs normalization + {"\u3332\u093C", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u093C\u3099", ""}}, + {"\u320E", []string{"\x28", "\uAC00", "\x29"}}, + + // last segment should be copied to start of buffer. + {"\ufdfa", []string{"\u0635", "\u0644", "\u0649", " ", "\u0627", "\u0644", "\u0644", "\u0647", " ", "\u0639", "\u0644", "\u064a", "\u0647", " ", "\u0648", "\u0633", "\u0644", "\u0645", ""}}, + {"\ufdfa" + grave(30), []string{"\u0635", "\u0644", "\u0649", " ", "\u0627", "\u0644", "\u0644", "\u0647", " ", "\u0639", "\u0644", "\u064a", "\u0647", " ", "\u0648", "\u0633", "\u0644", "\u0645" + grave(30), ""}}, + {"\uFDFA" + grave(64), []string{"\u0635", "\u0644", "\u0649", " ", "\u0627", "\u0644", "\u0644", "\u0647", " ", "\u0639", "\u0644", "\u064a", "\u0647", " ", "\u0648", "\u0633", "\u0644", "\u0645" + grave(30), cgj + grave(30), cgj + grave(4), ""}}, + + // Hangul and Jamo are grouped togeter. + {"\uAC00", []string{"\u1100\u1161", ""}}, + {"\uAC01", []string{"\u1100\u1161\u11A8", ""}}, + {"\u1100\u1161", []string{"\u1100\u1161", ""}}, +} + +// Note that, by design, segmentation is equal for composing and decomposing forms. +func TestIterSegmentation(t *testing.T) { + segmentTest(t, "SegmentTestD", NFD, segmentTests) + segmentTest(t, "SegmentTestC", NFC, segmentTests) + segmentTest(t, "SegmentTestKD", NFKD, segmentTestsK) + segmentTest(t, "SegmentTestKC", NFKC, segmentTestsK) +} + +func segmentTest(t *testing.T, name string, f Form, tests []SegmentTest) { + iter := Iter{} + for i, tt := range tests { + iter.InitString(f, tt.in) + for j, seg := range tt.out { + if seg == "" { + if !iter.Done() { + res := string(iter.Next()) + t.Errorf(`%s:%d:%d: expected Done()==true, found segment %+q`, name, i, j, res) + } + continue + } + if iter.Done() { + t.Errorf("%s:%d:%d: Done()==true, want false", name, i, j) + } + seg = f.String(seg) + if res := string(iter.Next()); res != seg { + t.Errorf(`%s:%d:%d" segment was %+q (%d); want %+q (%d)`, name, i, j, pc(res), len(res), pc(seg), len(seg)) + } + } + } +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/maketables.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/maketables.go new file mode 100644 index 000000000..07bdff6bd --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/maketables.go @@ -0,0 +1,978 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Normalization table generator. +// Data read from the web. +// See forminfo.go for a description of the trie values associated with each rune. + +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "log" + "sort" + "strconv" + "strings" + + "golang.org/x/text/internal/gen" + "golang.org/x/text/internal/triegen" + "golang.org/x/text/internal/ucd" +) + +func main() { + gen.Init() + loadUnicodeData() + compactCCC() + loadCompositionExclusions() + completeCharFields(FCanonical) + completeCharFields(FCompatibility) + computeNonStarterCounts() + verifyComputed() + printChars() + if *test { + testDerived() + printTestdata() + } else { + makeTables() + } +} + +var ( + tablelist = flag.String("tables", + "all", + "comma-separated list of which tables to generate; "+ + "can be 'decomp', 'recomp', 'info' and 'all'") + test = flag.Bool("test", + false, + "test existing tables against DerivedNormalizationProps and generate test data for regression testing") + verbose = flag.Bool("verbose", + false, + "write data to stdout as it is parsed") +) + +const MaxChar = 0x10FFFF // anything above this shouldn't exist + +// Quick Check properties of runes allow us to quickly +// determine whether a rune may occur in a normal form. +// For a given normal form, a rune may be guaranteed to occur +// verbatim (QC=Yes), may or may not combine with another +// rune (QC=Maybe), or may not occur (QC=No). +type QCResult int + +const ( + QCUnknown QCResult = iota + QCYes + QCNo + QCMaybe +) + +func (r QCResult) String() string { + switch r { + case QCYes: + return "Yes" + case QCNo: + return "No" + case QCMaybe: + return "Maybe" + } + return "***UNKNOWN***" +} + +const ( + FCanonical = iota // NFC or NFD + FCompatibility // NFKC or NFKD + FNumberOfFormTypes +) + +const ( + MComposed = iota // NFC or NFKC + MDecomposed // NFD or NFKD + MNumberOfModes +) + +// This contains only the properties we're interested in. +type Char struct { + name string + codePoint rune // if zero, this index is not a valid code point. + ccc uint8 // canonical combining class + origCCC uint8 + excludeInComp bool // from CompositionExclusions.txt + compatDecomp bool // it has a compatibility expansion + + nTrailingNonStarters uint8 + nLeadingNonStarters uint8 // must be equal to trailing if non-zero + + forms [FNumberOfFormTypes]FormInfo // For FCanonical and FCompatibility + + state State +} + +var chars = make([]Char, MaxChar+1) +var cccMap = make(map[uint8]uint8) + +func (c Char) String() string { + buf := new(bytes.Buffer) + + fmt.Fprintf(buf, "%U [%s]:\n", c.codePoint, c.name) + fmt.Fprintf(buf, " ccc: %v\n", c.ccc) + fmt.Fprintf(buf, " excludeInComp: %v\n", c.excludeInComp) + fmt.Fprintf(buf, " compatDecomp: %v\n", c.compatDecomp) + fmt.Fprintf(buf, " state: %v\n", c.state) + fmt.Fprintf(buf, " NFC:\n") + fmt.Fprint(buf, c.forms[FCanonical]) + fmt.Fprintf(buf, " NFKC:\n") + fmt.Fprint(buf, c.forms[FCompatibility]) + + return buf.String() +} + +// In UnicodeData.txt, some ranges are marked like this: +// 3400;;Lo;0;L;;;;;N;;;;; +// 4DB5;;Lo;0;L;;;;;N;;;;; +// parseCharacter keeps a state variable indicating the weirdness. +type State int + +const ( + SNormal State = iota // known to be zero for the type + SFirst + SLast + SMissing +) + +var lastChar = rune('\u0000') + +func (c Char) isValid() bool { + return c.codePoint != 0 && c.state != SMissing +} + +type FormInfo struct { + quickCheck [MNumberOfModes]QCResult // index: MComposed or MDecomposed + verified [MNumberOfModes]bool // index: MComposed or MDecomposed + + combinesForward bool // May combine with rune on the right + combinesBackward bool // May combine with rune on the left + isOneWay bool // Never appears in result + inDecomp bool // Some decompositions result in this char. + decomp Decomposition + expandedDecomp Decomposition +} + +func (f FormInfo) String() string { + buf := bytes.NewBuffer(make([]byte, 0)) + + fmt.Fprintf(buf, " quickCheck[C]: %v\n", f.quickCheck[MComposed]) + fmt.Fprintf(buf, " quickCheck[D]: %v\n", f.quickCheck[MDecomposed]) + fmt.Fprintf(buf, " cmbForward: %v\n", f.combinesForward) + fmt.Fprintf(buf, " cmbBackward: %v\n", f.combinesBackward) + fmt.Fprintf(buf, " isOneWay: %v\n", f.isOneWay) + fmt.Fprintf(buf, " inDecomp: %v\n", f.inDecomp) + fmt.Fprintf(buf, " decomposition: %X\n", f.decomp) + fmt.Fprintf(buf, " expandedDecomp: %X\n", f.expandedDecomp) + + return buf.String() +} + +type Decomposition []rune + +func parseDecomposition(s string, skipfirst bool) (a []rune, err error) { + decomp := strings.Split(s, " ") + if len(decomp) > 0 && skipfirst { + decomp = decomp[1:] + } + for _, d := range decomp { + point, err := strconv.ParseUint(d, 16, 64) + if err != nil { + return a, err + } + a = append(a, rune(point)) + } + return a, nil +} + +func loadUnicodeData() { + f := gen.OpenUCDFile("UnicodeData.txt") + defer f.Close() + p := ucd.New(f) + for p.Next() { + r := p.Rune(ucd.CodePoint) + char := &chars[r] + + char.ccc = uint8(p.Uint(ucd.CanonicalCombiningClass)) + decmap := p.String(ucd.DecompMapping) + + exp, err := parseDecomposition(decmap, false) + isCompat := false + if err != nil { + if len(decmap) > 0 { + exp, err = parseDecomposition(decmap, true) + if err != nil { + log.Fatalf(`%U: bad decomp |%v|: "%s"`, r, decmap, err) + } + isCompat = true + } + } + + char.name = p.String(ucd.Name) + char.codePoint = r + char.forms[FCompatibility].decomp = exp + if !isCompat { + char.forms[FCanonical].decomp = exp + } else { + char.compatDecomp = true + } + if len(decmap) > 0 { + char.forms[FCompatibility].decomp = exp + } + } + if err := p.Err(); err != nil { + log.Fatal(err) + } +} + +// compactCCC converts the sparse set of CCC values to a continguous one, +// reducing the number of bits needed from 8 to 6. +func compactCCC() { + m := make(map[uint8]uint8) + for i := range chars { + c := &chars[i] + m[c.ccc] = 0 + } + cccs := []int{} + for v, _ := range m { + cccs = append(cccs, int(v)) + } + sort.Ints(cccs) + for i, c := range cccs { + cccMap[uint8(i)] = uint8(c) + m[uint8(c)] = uint8(i) + } + for i := range chars { + c := &chars[i] + c.origCCC = c.ccc + c.ccc = m[c.ccc] + } + if len(m) >= 1<<6 { + log.Fatalf("too many difference CCC values: %d >= 64", len(m)) + } +} + +// CompositionExclusions.txt has form: +// 0958 # ... +// See http://unicode.org/reports/tr44/ for full explanation +func loadCompositionExclusions() { + f := gen.OpenUCDFile("CompositionExclusions.txt") + defer f.Close() + p := ucd.New(f) + for p.Next() { + c := &chars[p.Rune(0)] + if c.excludeInComp { + log.Fatalf("%U: Duplicate entry in exclusions.", c.codePoint) + } + c.excludeInComp = true + } + if e := p.Err(); e != nil { + log.Fatal(e) + } +} + +// hasCompatDecomp returns true if any of the recursive +// decompositions contains a compatibility expansion. +// In this case, the character may not occur in NFK*. +func hasCompatDecomp(r rune) bool { + c := &chars[r] + if c.compatDecomp { + return true + } + for _, d := range c.forms[FCompatibility].decomp { + if hasCompatDecomp(d) { + return true + } + } + return false +} + +// Hangul related constants. +const ( + HangulBase = 0xAC00 + HangulEnd = 0xD7A4 // hangulBase + Jamo combinations (19 * 21 * 28) + + JamoLBase = 0x1100 + JamoLEnd = 0x1113 + JamoVBase = 0x1161 + JamoVEnd = 0x1176 + JamoTBase = 0x11A8 + JamoTEnd = 0x11C3 + + JamoLVTCount = 19 * 21 * 28 + JamoTCount = 28 +) + +func isHangul(r rune) bool { + return HangulBase <= r && r < HangulEnd +} + +func isHangulWithoutJamoT(r rune) bool { + if !isHangul(r) { + return false + } + r -= HangulBase + return r < JamoLVTCount && r%JamoTCount == 0 +} + +func ccc(r rune) uint8 { + return chars[r].ccc +} + +// Insert a rune in a buffer, ordered by Canonical Combining Class. +func insertOrdered(b Decomposition, r rune) Decomposition { + n := len(b) + b = append(b, 0) + cc := ccc(r) + if cc > 0 { + // Use bubble sort. + for ; n > 0; n-- { + if ccc(b[n-1]) <= cc { + break + } + b[n] = b[n-1] + } + } + b[n] = r + return b +} + +// Recursively decompose. +func decomposeRecursive(form int, r rune, d Decomposition) Decomposition { + dcomp := chars[r].forms[form].decomp + if len(dcomp) == 0 { + return insertOrdered(d, r) + } + for _, c := range dcomp { + d = decomposeRecursive(form, c, d) + } + return d +} + +func completeCharFields(form int) { + // Phase 0: pre-expand decomposition. + for i := range chars { + f := &chars[i].forms[form] + if len(f.decomp) == 0 { + continue + } + exp := make(Decomposition, 0) + for _, c := range f.decomp { + exp = decomposeRecursive(form, c, exp) + } + f.expandedDecomp = exp + } + + // Phase 1: composition exclusion, mark decomposition. + for i := range chars { + c := &chars[i] + f := &c.forms[form] + + // Marks script-specific exclusions and version restricted. + f.isOneWay = c.excludeInComp + + // Singletons + f.isOneWay = f.isOneWay || len(f.decomp) == 1 + + // Non-starter decompositions + if len(f.decomp) > 1 { + chk := c.ccc != 0 || chars[f.decomp[0]].ccc != 0 + f.isOneWay = f.isOneWay || chk + } + + // Runes that decompose into more than two runes. + f.isOneWay = f.isOneWay || len(f.decomp) > 2 + + if form == FCompatibility { + f.isOneWay = f.isOneWay || hasCompatDecomp(c.codePoint) + } + + for _, r := range f.decomp { + chars[r].forms[form].inDecomp = true + } + } + + // Phase 2: forward and backward combining. + for i := range chars { + c := &chars[i] + f := &c.forms[form] + + if !f.isOneWay && len(f.decomp) == 2 { + f0 := &chars[f.decomp[0]].forms[form] + f1 := &chars[f.decomp[1]].forms[form] + if !f0.isOneWay { + f0.combinesForward = true + } + if !f1.isOneWay { + f1.combinesBackward = true + } + } + if isHangulWithoutJamoT(rune(i)) { + f.combinesForward = true + } + } + + // Phase 3: quick check values. + for i := range chars { + c := &chars[i] + f := &c.forms[form] + + switch { + case len(f.decomp) > 0: + f.quickCheck[MDecomposed] = QCNo + case isHangul(rune(i)): + f.quickCheck[MDecomposed] = QCNo + default: + f.quickCheck[MDecomposed] = QCYes + } + switch { + case f.isOneWay: + f.quickCheck[MComposed] = QCNo + case (i & 0xffff00) == JamoLBase: + f.quickCheck[MComposed] = QCYes + if JamoLBase <= i && i < JamoLEnd { + f.combinesForward = true + } + if JamoVBase <= i && i < JamoVEnd { + f.quickCheck[MComposed] = QCMaybe + f.combinesBackward = true + f.combinesForward = true + } + if JamoTBase <= i && i < JamoTEnd { + f.quickCheck[MComposed] = QCMaybe + f.combinesBackward = true + } + case !f.combinesBackward: + f.quickCheck[MComposed] = QCYes + default: + f.quickCheck[MComposed] = QCMaybe + } + } +} + +func computeNonStarterCounts() { + // Phase 4: leading and trailing non-starter count + for i := range chars { + c := &chars[i] + + runes := []rune{rune(i)} + // We always use FCompatibility so that the CGJ insertion points do not + // change for repeated normalizations with different forms. + if exp := c.forms[FCompatibility].expandedDecomp; len(exp) > 0 { + runes = exp + } + // We consider runes that combine backwards to be non-starters for the + // purpose of Stream-Safe Text Processing. + for _, r := range runes { + if cr := &chars[r]; cr.ccc == 0 && !cr.forms[FCompatibility].combinesBackward { + break + } + c.nLeadingNonStarters++ + } + for i := len(runes) - 1; i >= 0; i-- { + if cr := &chars[runes[i]]; cr.ccc == 0 && !cr.forms[FCompatibility].combinesBackward { + break + } + c.nTrailingNonStarters++ + } + if c.nTrailingNonStarters > 3 { + log.Fatalf("%U: Decomposition with more than 3 (%d) trailing modifiers (%U)", i, c.nTrailingNonStarters, runes) + } + + if isHangul(rune(i)) { + c.nTrailingNonStarters = 2 + if isHangulWithoutJamoT(rune(i)) { + c.nTrailingNonStarters = 1 + } + } + + if l, t := c.nLeadingNonStarters, c.nTrailingNonStarters; l > 0 && l != t { + log.Fatalf("%U: number of leading and trailing non-starters should be equal (%d vs %d)", i, l, t) + } + if t := c.nTrailingNonStarters; t > 3 { + log.Fatalf("%U: number of trailing non-starters is %d > 3", t) + } + } +} + +func printBytes(w io.Writer, b []byte, name string) { + fmt.Fprintf(w, "// %s: %d bytes\n", name, len(b)) + fmt.Fprintf(w, "var %s = [...]byte {", name) + for i, c := range b { + switch { + case i%64 == 0: + fmt.Fprintf(w, "\n// Bytes %x - %x\n", i, i+63) + case i%8 == 0: + fmt.Fprintf(w, "\n") + } + fmt.Fprintf(w, "0x%.2X, ", c) + } + fmt.Fprint(w, "\n}\n\n") +} + +// See forminfo.go for format. +func makeEntry(f *FormInfo, c *Char) uint16 { + e := uint16(0) + if r := c.codePoint; HangulBase <= r && r < HangulEnd { + e |= 0x40 + } + if f.combinesForward { + e |= 0x20 + } + if f.quickCheck[MDecomposed] == QCNo { + e |= 0x4 + } + switch f.quickCheck[MComposed] { + case QCYes: + case QCNo: + e |= 0x10 + case QCMaybe: + e |= 0x18 + default: + log.Fatalf("Illegal quickcheck value %v.", f.quickCheck[MComposed]) + } + e |= uint16(c.nTrailingNonStarters) + return e +} + +// decompSet keeps track of unique decompositions, grouped by whether +// the decomposition is followed by a trailing and/or leading CCC. +type decompSet [7]map[string]bool + +const ( + normalDecomp = iota + firstMulti + firstCCC + endMulti + firstLeadingCCC + firstCCCZeroExcept + firstStarterWithNLead + lastDecomp +) + +var cname = []string{"firstMulti", "firstCCC", "endMulti", "firstLeadingCCC", "firstCCCZeroExcept", "firstStarterWithNLead", "lastDecomp"} + +func makeDecompSet() decompSet { + m := decompSet{} + for i := range m { + m[i] = make(map[string]bool) + } + return m +} +func (m *decompSet) insert(key int, s string) { + m[key][s] = true +} + +func printCharInfoTables(w io.Writer) int { + mkstr := func(r rune, f *FormInfo) (int, string) { + d := f.expandedDecomp + s := string([]rune(d)) + if max := 1 << 6; len(s) >= max { + const msg = "%U: too many bytes in decomposition: %d >= %d" + log.Fatalf(msg, r, len(s), max) + } + head := uint8(len(s)) + if f.quickCheck[MComposed] != QCYes { + head |= 0x40 + } + if f.combinesForward { + head |= 0x80 + } + s = string([]byte{head}) + s + + lccc := ccc(d[0]) + tccc := ccc(d[len(d)-1]) + cc := ccc(r) + if cc != 0 && lccc == 0 && tccc == 0 { + log.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", r, cc) + } + if tccc < lccc && lccc != 0 { + const msg = "%U: lccc (%d) must be <= tcc (%d)" + log.Fatalf(msg, r, lccc, tccc) + } + index := normalDecomp + nTrail := chars[r].nTrailingNonStarters + if tccc > 0 || lccc > 0 || nTrail > 0 { + tccc <<= 2 + tccc |= nTrail + s += string([]byte{tccc}) + index = endMulti + for _, r := range d[1:] { + if ccc(r) == 0 { + index = firstCCC + } + } + if lccc > 0 { + s += string([]byte{lccc}) + if index == firstCCC { + log.Fatalf("%U: multi-segment decomposition not supported for decompositions with leading CCC != 0", r) + } + index = firstLeadingCCC + } + if cc != lccc { + if cc != 0 { + log.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", r, cc) + } + index = firstCCCZeroExcept + } + } else if len(d) > 1 { + index = firstMulti + } + return index, s + } + + decompSet := makeDecompSet() + const nLeadStr = "\x00\x01" // 0-byte length and tccc with nTrail. + decompSet.insert(firstStarterWithNLead, nLeadStr) + + // Store the uniqued decompositions in a byte buffer, + // preceded by their byte length. + for _, c := range chars { + for _, f := range c.forms { + if len(f.expandedDecomp) == 0 { + continue + } + if f.combinesBackward { + log.Fatalf("%U: combinesBackward and decompose", c.codePoint) + } + index, s := mkstr(c.codePoint, &f) + decompSet.insert(index, s) + } + } + + decompositions := bytes.NewBuffer(make([]byte, 0, 10000)) + size := 0 + positionMap := make(map[string]uint16) + decompositions.WriteString("\000") + fmt.Fprintln(w, "const (") + for i, m := range decompSet { + sa := []string{} + for s := range m { + sa = append(sa, s) + } + sort.Strings(sa) + for _, s := range sa { + p := decompositions.Len() + decompositions.WriteString(s) + positionMap[s] = uint16(p) + } + if cname[i] != "" { + fmt.Fprintf(w, "%s = 0x%X\n", cname[i], decompositions.Len()) + } + } + fmt.Fprintln(w, "maxDecomp = 0x8000") + fmt.Fprintln(w, ")") + b := decompositions.Bytes() + printBytes(w, b, "decomps") + size += len(b) + + varnames := []string{"nfc", "nfkc"} + for i := 0; i < FNumberOfFormTypes; i++ { + trie := triegen.NewTrie(varnames[i]) + + for r, c := range chars { + f := c.forms[i] + d := f.expandedDecomp + if len(d) != 0 { + _, key := mkstr(c.codePoint, &f) + trie.Insert(rune(r), uint64(positionMap[key])) + if c.ccc != ccc(d[0]) { + // We assume the lead ccc of a decomposition !=0 in this case. + if ccc(d[0]) == 0 { + log.Fatalf("Expected leading CCC to be non-zero; ccc is %d", c.ccc) + } + } + } else if c.nLeadingNonStarters > 0 && len(f.expandedDecomp) == 0 && c.ccc == 0 && !f.combinesBackward { + // Handle cases where it can't be detected that the nLead should be equal + // to nTrail. + trie.Insert(c.codePoint, uint64(positionMap[nLeadStr])) + } else if v := makeEntry(&f, &c)<<8 | uint16(c.ccc); v != 0 { + trie.Insert(c.codePoint, uint64(0x8000|v)) + } + } + sz, err := trie.Gen(w, triegen.Compact(&normCompacter{name: varnames[i]})) + if err != nil { + log.Fatal(err) + } + size += sz + } + return size +} + +func contains(sa []string, s string) bool { + for _, a := range sa { + if a == s { + return true + } + } + return false +} + +func makeTables() { + w := &bytes.Buffer{} + + size := 0 + if *tablelist == "" { + return + } + list := strings.Split(*tablelist, ",") + if *tablelist == "all" { + list = []string{"recomp", "info"} + } + + // Compute maximum decomposition size. + max := 0 + for _, c := range chars { + if n := len(string(c.forms[FCompatibility].expandedDecomp)); n > max { + max = n + } + } + + fmt.Fprintln(w, "const (") + fmt.Fprintln(w, "\t// Version is the Unicode edition from which the tables are derived.") + fmt.Fprintf(w, "\tVersion = %q\n", gen.UnicodeVersion()) + fmt.Fprintln(w) + fmt.Fprintln(w, "\t// MaxTransformChunkSize indicates the maximum number of bytes that Transform") + fmt.Fprintln(w, "\t// may need to write atomically for any Form. Making a destination buffer at") + fmt.Fprintln(w, "\t// least this size ensures that Transform can always make progress and that") + fmt.Fprintln(w, "\t// the user does not need to grow the buffer on an ErrShortDst.") + fmt.Fprintf(w, "\tMaxTransformChunkSize = %d+maxNonStarters*4\n", len(string(0x034F))+max) + fmt.Fprintln(w, ")\n") + + // Print the CCC remap table. + size += len(cccMap) + fmt.Fprintf(w, "var ccc = [%d]uint8{", len(cccMap)) + for i := 0; i < len(cccMap); i++ { + if i%8 == 0 { + fmt.Fprintln(w) + } + fmt.Fprintf(w, "%3d, ", cccMap[uint8(i)]) + } + fmt.Fprintln(w, "\n}\n") + + if contains(list, "info") { + size += printCharInfoTables(w) + } + + if contains(list, "recomp") { + // Note that we use 32 bit keys, instead of 64 bit. + // This clips the bits of three entries, but we know + // this won't cause a collision. The compiler will catch + // any changes made to UnicodeData.txt that introduces + // a collision. + // Note that the recomposition map for NFC and NFKC + // are identical. + + // Recomposition map + nrentries := 0 + for _, c := range chars { + f := c.forms[FCanonical] + if !f.isOneWay && len(f.decomp) > 0 { + nrentries++ + } + } + sz := nrentries * 8 + size += sz + fmt.Fprintf(w, "// recompMap: %d bytes (entries only)\n", sz) + fmt.Fprintln(w, "var recompMap = map[uint32]rune{") + for i, c := range chars { + f := c.forms[FCanonical] + d := f.decomp + if !f.isOneWay && len(d) > 0 { + key := uint32(uint16(d[0]))<<16 + uint32(uint16(d[1])) + fmt.Fprintf(w, "0x%.8X: 0x%.4X,\n", key, i) + } + } + fmt.Fprintf(w, "}\n\n") + } + + fmt.Fprintf(w, "// Total size of tables: %dKB (%d bytes)\n", (size+512)/1024, size) + gen.WriteGoFile("tables.go", "norm", w.Bytes()) +} + +func printChars() { + if *verbose { + for _, c := range chars { + if !c.isValid() || c.state == SMissing { + continue + } + fmt.Println(c) + } + } +} + +// verifyComputed does various consistency tests. +func verifyComputed() { + for i, c := range chars { + for _, f := range c.forms { + isNo := (f.quickCheck[MDecomposed] == QCNo) + if (len(f.decomp) > 0) != isNo && !isHangul(rune(i)) { + log.Fatalf("%U: NF*D QC must be No if rune decomposes", i) + } + + isMaybe := f.quickCheck[MComposed] == QCMaybe + if f.combinesBackward != isMaybe { + log.Fatalf("%U: NF*C QC must be Maybe if combinesBackward", i) + } + if len(f.decomp) > 0 && f.combinesForward && isMaybe { + log.Fatalf("%U: NF*C QC must be Yes or No if combinesForward and decomposes", i) + } + + if len(f.expandedDecomp) != 0 { + continue + } + if a, b := c.nLeadingNonStarters > 0, (c.ccc > 0 || f.combinesBackward); a != b { + // We accept these runes to be treated differently (it only affects + // segment breaking in iteration, most likely on improper use), but + // reconsider if more characters are added. + // U+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L; 3099;;;;N;;;;; + // U+FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L; 309A;;;;N;;;;; + // U+3133 HANGUL LETTER KIYEOK-SIOS;Lo;0;L; 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;; + // U+318E HANGUL LETTER ARAEAE;Lo;0;L; 11A1;;;;N;HANGUL LETTER ALAE AE;;;; + // U+FFA3 HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L; 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;; + // U+FFDC HALFWIDTH HANGUL LETTER I;Lo;0;L; 3163;;;;N;;;;; + if i != 0xFF9E && i != 0xFF9F && !(0x3133 <= i && i <= 0x318E) && !(0xFFA3 <= i && i <= 0xFFDC) { + log.Fatalf("%U: nLead was %v; want %v", i, a, b) + } + } + } + nfc := c.forms[FCanonical] + nfkc := c.forms[FCompatibility] + if nfc.combinesBackward != nfkc.combinesBackward { + log.Fatalf("%U: Cannot combine combinesBackward\n", c.codePoint) + } + } +} + +// Use values in DerivedNormalizationProps.txt to compare against the +// values we computed. +// DerivedNormalizationProps.txt has form: +// 00C0..00C5 ; NFD_QC; N # ... +// 0374 ; NFD_QC; N # ... +// See http://unicode.org/reports/tr44/ for full explanation +func testDerived() { + f := gen.OpenUCDFile("DerivedNormalizationProps.txt") + defer f.Close() + p := ucd.New(f) + for p.Next() { + r := p.Rune(0) + c := &chars[r] + + var ftype, mode int + qt := p.String(1) + switch qt { + case "NFC_QC": + ftype, mode = FCanonical, MComposed + case "NFD_QC": + ftype, mode = FCanonical, MDecomposed + case "NFKC_QC": + ftype, mode = FCompatibility, MComposed + case "NFKD_QC": + ftype, mode = FCompatibility, MDecomposed + default: + continue + } + var qr QCResult + switch p.String(2) { + case "Y": + qr = QCYes + case "N": + qr = QCNo + case "M": + qr = QCMaybe + default: + log.Fatalf(`Unexpected quick check value "%s"`, p.String(2)) + } + if got := c.forms[ftype].quickCheck[mode]; got != qr { + log.Printf("%U: FAILED %s (was %v need %v)\n", r, qt, got, qr) + } + c.forms[ftype].verified[mode] = true + } + if err := p.Err(); err != nil { + log.Fatal(err) + } + // Any unspecified value must be QCYes. Verify this. + for i, c := range chars { + for j, fd := range c.forms { + for k, qr := range fd.quickCheck { + if !fd.verified[k] && qr != QCYes { + m := "%U: FAIL F:%d M:%d (was %v need Yes) %s\n" + log.Printf(m, i, j, k, qr, c.name) + } + } + } + } +} + +var testHeader = `const ( + Yes = iota + No + Maybe +) + +type formData struct { + qc uint8 + combinesForward bool + decomposition string +} + +type runeData struct { + r rune + ccc uint8 + nLead uint8 + nTrail uint8 + f [2]formData // 0: canonical; 1: compatibility +} + +func f(qc uint8, cf bool, dec string) [2]formData { + return [2]formData{{qc, cf, dec}, {qc, cf, dec}} +} + +func g(qc, qck uint8, cf, cfk bool, d, dk string) [2]formData { + return [2]formData{{qc, cf, d}, {qck, cfk, dk}} +} + +var testData = []runeData{ +` + +func printTestdata() { + type lastInfo struct { + ccc uint8 + nLead uint8 + nTrail uint8 + f string + } + + last := lastInfo{} + w := &bytes.Buffer{} + fmt.Fprintf(w, testHeader) + for r, c := range chars { + f := c.forms[FCanonical] + qc, cf, d := f.quickCheck[MComposed], f.combinesForward, string(f.expandedDecomp) + f = c.forms[FCompatibility] + qck, cfk, dk := f.quickCheck[MComposed], f.combinesForward, string(f.expandedDecomp) + s := "" + if d == dk && qc == qck && cf == cfk { + s = fmt.Sprintf("f(%s, %v, %q)", qc, cf, d) + } else { + s = fmt.Sprintf("g(%s, %s, %v, %v, %q, %q)", qc, qck, cf, cfk, d, dk) + } + current := lastInfo{c.ccc, c.nLeadingNonStarters, c.nTrailingNonStarters, s} + if last != current { + fmt.Fprintf(w, "\t{0x%x, %d, %d, %d, %s},\n", r, c.origCCC, c.nLeadingNonStarters, c.nTrailingNonStarters, s) + last = current + } + } + fmt.Fprintln(w, "}") + gen.WriteGoFile("data_test.go", "norm", w.Bytes()) +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/norm_test.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/norm_test.go new file mode 100644 index 000000000..12dacfcf3 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/norm_test.go @@ -0,0 +1,14 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm_test + +import ( + "testing" +) + +func TestPlaceHolder(t *testing.T) { + // Does nothing, just allows the Makefile to be canonical + // while waiting for the package itself to be written. +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/normalize.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/normalize.go new file mode 100644 index 000000000..bcf8450d0 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/normalize.go @@ -0,0 +1,527 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run maketables.go triegen.go +//go:generate go run maketables.go triegen.go -test + +// Package norm contains types and functions for normalizing Unicode strings. +package norm + +import "unicode/utf8" + +// A Form denotes a canonical representation of Unicode code points. +// The Unicode-defined normalization and equivalence forms are: +// +// NFC Unicode Normalization Form C +// NFD Unicode Normalization Form D +// NFKC Unicode Normalization Form KC +// NFKD Unicode Normalization Form KD +// +// For a Form f, this documentation uses the notation f(x) to mean +// the bytes or string x converted to the given form. +// A position n in x is called a boundary if conversion to the form can +// proceed independently on both sides: +// f(x) == append(f(x[0:n]), f(x[n:])...) +// +// References: http://unicode.org/reports/tr15/ and +// http://unicode.org/notes/tn5/. +type Form int + +const ( + NFC Form = iota + NFD + NFKC + NFKD +) + +// Bytes returns f(b). May return b if f(b) = b. +func (f Form) Bytes(b []byte) []byte { + src := inputBytes(b) + ft := formTable[f] + n, ok := ft.quickSpan(src, 0, len(b), true) + if ok { + return b + } + out := make([]byte, n, len(b)) + copy(out, b[0:n]) + rb := reorderBuffer{f: *ft, src: src, nsrc: len(b), out: out, flushF: appendFlush} + return doAppendInner(&rb, n) +} + +// String returns f(s). +func (f Form) String(s string) string { + src := inputString(s) + ft := formTable[f] + n, ok := ft.quickSpan(src, 0, len(s), true) + if ok { + return s + } + out := make([]byte, n, len(s)) + copy(out, s[0:n]) + rb := reorderBuffer{f: *ft, src: src, nsrc: len(s), out: out, flushF: appendFlush} + return string(doAppendInner(&rb, n)) +} + +// IsNormal returns true if b == f(b). +func (f Form) IsNormal(b []byte) bool { + src := inputBytes(b) + ft := formTable[f] + bp, ok := ft.quickSpan(src, 0, len(b), true) + if ok { + return true + } + rb := reorderBuffer{f: *ft, src: src, nsrc: len(b)} + rb.setFlusher(nil, cmpNormalBytes) + for bp < len(b) { + rb.out = b[bp:] + if bp = decomposeSegment(&rb, bp, true); bp < 0 { + return false + } + bp, _ = rb.f.quickSpan(rb.src, bp, len(b), true) + } + return true +} + +func cmpNormalBytes(rb *reorderBuffer) bool { + b := rb.out + for i := 0; i < rb.nrune; i++ { + info := rb.rune[i] + if int(info.size) > len(b) { + return false + } + p := info.pos + pe := p + info.size + for ; p < pe; p++ { + if b[0] != rb.byte[p] { + return false + } + b = b[1:] + } + } + return true +} + +// IsNormalString returns true if s == f(s). +func (f Form) IsNormalString(s string) bool { + src := inputString(s) + ft := formTable[f] + bp, ok := ft.quickSpan(src, 0, len(s), true) + if ok { + return true + } + rb := reorderBuffer{f: *ft, src: src, nsrc: len(s)} + rb.setFlusher(nil, func(rb *reorderBuffer) bool { + for i := 0; i < rb.nrune; i++ { + info := rb.rune[i] + if bp+int(info.size) > len(s) { + return false + } + p := info.pos + pe := p + info.size + for ; p < pe; p++ { + if s[bp] != rb.byte[p] { + return false + } + bp++ + } + } + return true + }) + for bp < len(s) { + if bp = decomposeSegment(&rb, bp, true); bp < 0 { + return false + } + bp, _ = rb.f.quickSpan(rb.src, bp, len(s), true) + } + return true +} + +// patchTail fixes a case where a rune may be incorrectly normalized +// if it is followed by illegal continuation bytes. It returns the +// patched buffer and whether the decomposition is still in progress. +func patchTail(rb *reorderBuffer) bool { + info, p := lastRuneStart(&rb.f, rb.out) + if p == -1 || info.size == 0 { + return true + } + end := p + int(info.size) + extra := len(rb.out) - end + if extra > 0 { + // Potentially allocating memory. However, this only + // happens with ill-formed UTF-8. + x := make([]byte, 0) + x = append(x, rb.out[len(rb.out)-extra:]...) + rb.out = rb.out[:end] + decomposeToLastBoundary(rb) + rb.doFlush() + rb.out = append(rb.out, x...) + return false + } + buf := rb.out[p:] + rb.out = rb.out[:p] + decomposeToLastBoundary(rb) + if s := rb.ss.next(info); s == ssStarter { + rb.doFlush() + rb.ss.first(info) + } else if s == ssOverflow { + rb.doFlush() + rb.insertCGJ() + rb.ss = 0 + } + rb.insertUnsafe(inputBytes(buf), 0, info) + return true +} + +func appendQuick(rb *reorderBuffer, i int) int { + if rb.nsrc == i { + return i + } + end, _ := rb.f.quickSpan(rb.src, i, rb.nsrc, true) + rb.out = rb.src.appendSlice(rb.out, i, end) + return end +} + +// Append returns f(append(out, b...)). +// The buffer out must be nil, empty, or equal to f(out). +func (f Form) Append(out []byte, src ...byte) []byte { + return f.doAppend(out, inputBytes(src), len(src)) +} + +func (f Form) doAppend(out []byte, src input, n int) []byte { + if n == 0 { + return out + } + ft := formTable[f] + // Attempt to do a quickSpan first so we can avoid initializing the reorderBuffer. + if len(out) == 0 { + p, _ := ft.quickSpan(src, 0, n, true) + out = src.appendSlice(out, 0, p) + if p == n { + return out + } + rb := reorderBuffer{f: *ft, src: src, nsrc: n, out: out, flushF: appendFlush} + return doAppendInner(&rb, p) + } + rb := reorderBuffer{f: *ft, src: src, nsrc: n} + return doAppend(&rb, out, 0) +} + +func doAppend(rb *reorderBuffer, out []byte, p int) []byte { + rb.setFlusher(out, appendFlush) + src, n := rb.src, rb.nsrc + doMerge := len(out) > 0 + if q := src.skipContinuationBytes(p); q > p { + // Move leading non-starters to destination. + rb.out = src.appendSlice(rb.out, p, q) + p = q + doMerge = patchTail(rb) + } + fd := &rb.f + if doMerge { + var info Properties + if p < n { + info = fd.info(src, p) + if !info.BoundaryBefore() || info.nLeadingNonStarters() > 0 { + if p == 0 { + decomposeToLastBoundary(rb) + } + p = decomposeSegment(rb, p, true) + } + } + if info.size == 0 { + rb.doFlush() + // Append incomplete UTF-8 encoding. + return src.appendSlice(rb.out, p, n) + } + if rb.nrune > 0 { + return doAppendInner(rb, p) + } + } + p = appendQuick(rb, p) + return doAppendInner(rb, p) +} + +func doAppendInner(rb *reorderBuffer, p int) []byte { + for n := rb.nsrc; p < n; { + p = decomposeSegment(rb, p, true) + p = appendQuick(rb, p) + } + return rb.out +} + +// AppendString returns f(append(out, []byte(s))). +// The buffer out must be nil, empty, or equal to f(out). +func (f Form) AppendString(out []byte, src string) []byte { + return f.doAppend(out, inputString(src), len(src)) +} + +// QuickSpan returns a boundary n such that b[0:n] == f(b[0:n]). +// It is not guaranteed to return the largest such n. +func (f Form) QuickSpan(b []byte) int { + n, _ := formTable[f].quickSpan(inputBytes(b), 0, len(b), true) + return n +} + +// quickSpan returns a boundary n such that src[0:n] == f(src[0:n]) and +// whether any non-normalized parts were found. If atEOF is false, n will +// not point past the last segment if this segment might be become +// non-normalized by appending other runes. +func (f *formInfo) quickSpan(src input, i, end int, atEOF bool) (n int, ok bool) { + var lastCC uint8 + ss := streamSafe(0) + lastSegStart := i + for n = end; i < n; { + if j := src.skipASCII(i, n); i != j { + i = j + lastSegStart = i - 1 + lastCC = 0 + ss = 0 + continue + } + info := f.info(src, i) + if info.size == 0 { + if atEOF { + // include incomplete runes + return n, true + } + return lastSegStart, true + } + // This block needs to be before the next, because it is possible to + // have an overflow for runes that are starters (e.g. with U+FF9E). + switch ss.next(info) { + case ssStarter: + ss.first(info) + lastSegStart = i + case ssOverflow: + return lastSegStart, false + case ssSuccess: + if lastCC > info.ccc { + return lastSegStart, false + } + } + if f.composing { + if !info.isYesC() { + break + } + } else { + if !info.isYesD() { + break + } + } + lastCC = info.ccc + i += int(info.size) + } + if i == n { + if !atEOF { + n = lastSegStart + } + return n, true + } + return lastSegStart, false +} + +// QuickSpanString returns a boundary n such that b[0:n] == f(s[0:n]). +// It is not guaranteed to return the largest such n. +func (f Form) QuickSpanString(s string) int { + n, _ := formTable[f].quickSpan(inputString(s), 0, len(s), true) + return n +} + +// FirstBoundary returns the position i of the first boundary in b +// or -1 if b contains no boundary. +func (f Form) FirstBoundary(b []byte) int { + return f.firstBoundary(inputBytes(b), len(b)) +} + +func (f Form) firstBoundary(src input, nsrc int) int { + i := src.skipContinuationBytes(0) + if i >= nsrc { + return -1 + } + fd := formTable[f] + ss := streamSafe(0) + // We should call ss.first here, but we can't as the first rune is + // skipped already. This means FirstBoundary can't really determine + // CGJ insertion points correctly. Luckily it doesn't have to. + // TODO: consider adding NextBoundary + for { + info := fd.info(src, i) + if info.size == 0 { + return -1 + } + if s := ss.next(info); s != ssSuccess { + return i + } + i += int(info.size) + if i >= nsrc { + if !info.BoundaryAfter() && !ss.isMax() { + return -1 + } + return nsrc + } + } +} + +// FirstBoundaryInString returns the position i of the first boundary in s +// or -1 if s contains no boundary. +func (f Form) FirstBoundaryInString(s string) int { + return f.firstBoundary(inputString(s), len(s)) +} + +// LastBoundary returns the position i of the last boundary in b +// or -1 if b contains no boundary. +func (f Form) LastBoundary(b []byte) int { + return lastBoundary(formTable[f], b) +} + +func lastBoundary(fd *formInfo, b []byte) int { + i := len(b) + info, p := lastRuneStart(fd, b) + if p == -1 { + return -1 + } + if info.size == 0 { // ends with incomplete rune + if p == 0 { // starts with incomplete rune + return -1 + } + i = p + info, p = lastRuneStart(fd, b[:i]) + if p == -1 { // incomplete UTF-8 encoding or non-starter bytes without a starter + return i + } + } + if p+int(info.size) != i { // trailing non-starter bytes: illegal UTF-8 + return i + } + if info.BoundaryAfter() { + return i + } + ss := streamSafe(0) + v := ss.backwards(info) + for i = p; i >= 0 && v != ssStarter; i = p { + info, p = lastRuneStart(fd, b[:i]) + if v = ss.backwards(info); v == ssOverflow { + break + } + if p+int(info.size) != i { + if p == -1 { // no boundary found + return -1 + } + return i // boundary after an illegal UTF-8 encoding + } + } + return i +} + +// decomposeSegment scans the first segment in src into rb. It inserts 0x034f +// (Grapheme Joiner) when it encounters a sequence of more than 30 non-starters +// and returns the number of bytes consumed from src or iShortDst or iShortSrc. +func decomposeSegment(rb *reorderBuffer, sp int, atEOF bool) int { + // Force one character to be consumed. + info := rb.f.info(rb.src, sp) + if info.size == 0 { + return 0 + } + if rb.nrune > 0 { + if s := rb.ss.next(info); s == ssStarter { + goto end + } else if s == ssOverflow { + rb.insertCGJ() + goto end + } + } else { + rb.ss.first(info) + } + if err := rb.insertFlush(rb.src, sp, info); err != iSuccess { + return int(err) + } + for { + sp += int(info.size) + if sp >= rb.nsrc { + if !atEOF && !info.BoundaryAfter() { + return int(iShortSrc) + } + break + } + info = rb.f.info(rb.src, sp) + if info.size == 0 { + if !atEOF { + return int(iShortSrc) + } + break + } + if s := rb.ss.next(info); s == ssStarter { + break + } else if s == ssOverflow { + rb.insertCGJ() + break + } + if err := rb.insertFlush(rb.src, sp, info); err != iSuccess { + return int(err) + } + } +end: + if !rb.doFlush() { + return int(iShortDst) + } + return sp +} + +// lastRuneStart returns the runeInfo and position of the last +// rune in buf or the zero runeInfo and -1 if no rune was found. +func lastRuneStart(fd *formInfo, buf []byte) (Properties, int) { + p := len(buf) - 1 + for ; p >= 0 && !utf8.RuneStart(buf[p]); p-- { + } + if p < 0 { + return Properties{}, -1 + } + return fd.info(inputBytes(buf), p), p +} + +// decomposeToLastBoundary finds an open segment at the end of the buffer +// and scans it into rb. Returns the buffer minus the last segment. +func decomposeToLastBoundary(rb *reorderBuffer) { + fd := &rb.f + info, i := lastRuneStart(fd, rb.out) + if int(info.size) != len(rb.out)-i { + // illegal trailing continuation bytes + return + } + if info.BoundaryAfter() { + return + } + var add [maxNonStarters + 1]Properties // stores runeInfo in reverse order + padd := 0 + ss := streamSafe(0) + p := len(rb.out) + for { + add[padd] = info + v := ss.backwards(info) + if v == ssOverflow { + // Note that if we have an overflow, it the string we are appending to + // is not correctly normalized. In this case the behavior is undefined. + break + } + padd++ + p -= int(info.size) + if v == ssStarter || p < 0 { + break + } + info, i = lastRuneStart(fd, rb.out[:p]) + if int(info.size) != p-i { + break + } + } + rb.ss = ss + // Copy bytes for insertion as we may need to overwrite rb.out. + var buf [maxBufferSize * utf8.UTFMax]byte + cp := buf[:copy(buf[:], rb.out[p:])] + rb.out = rb.out[:p] + for padd--; padd >= 0; padd-- { + info = add[padd] + rb.insertUnsafe(inputBytes(cp), 0, info) + cp = cp[info.size:] + } +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/normalize_test.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/normalize_test.go new file mode 100644 index 000000000..8f2eac612 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/normalize_test.go @@ -0,0 +1,1142 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "bytes" + "flag" + "fmt" + "io" + "log" + "strings" + "testing" + "unicode/utf8" +) + +var ( + testn = flag.Int("testn", -1, "specific test number to run or -1 for all") +) + +// pc replaces any rune r that is repeated n times, for n > 1, with r{n}. +func pc(s string) []byte { + b := bytes.NewBuffer(make([]byte, 0, len(s))) + for i := 0; i < len(s); { + r, sz := utf8.DecodeRuneInString(s[i:]) + n := 0 + if sz == 1 { + // Special-case one-byte case to handle repetition for invalid UTF-8. + for c := s[i]; i+n < len(s) && s[i+n] == c; n++ { + } + } else { + for _, r2 := range s[i:] { + if r2 != r { + break + } + n++ + } + } + b.WriteString(s[i : i+sz]) + if n > 1 { + fmt.Fprintf(b, "{%d}", n) + } + i += sz * n + } + return b.Bytes() +} + +// pidx finds the index from which two strings start to differ, plus context. +// It returns the index and ellipsis if the index is greater than 0. +func pidx(a, b string) (i int, prefix string) { + for ; i < len(a) && i < len(b) && a[i] == b[i]; i++ { + } + if i < 8 { + return 0, "" + } + i -= 3 // ensure taking at least one full rune before the difference. + for k := i - 7; i > k && !utf8.RuneStart(a[i]); i-- { + } + return i, "..." +} + +type PositionTest struct { + input string + pos int + buffer string // expected contents of reorderBuffer, if applicable +} + +type positionFunc func(rb *reorderBuffer, s string) (int, []byte) + +func runPosTests(t *testing.T, name string, f Form, fn positionFunc, tests []PositionTest) { + rb := reorderBuffer{} + rb.init(f, nil) + for i, test := range tests { + rb.reset() + rb.src = inputString(test.input) + rb.nsrc = len(test.input) + pos, out := fn(&rb, test.input) + if pos != test.pos { + t.Errorf("%s:%d: position is %d; want %d", name, i, pos, test.pos) + } + if outs := string(out); outs != test.buffer { + k, pfx := pidx(outs, test.buffer) + t.Errorf("%s:%d: buffer \nwas %s%+q; \nwant %s%+q", name, i, pfx, pc(outs[k:]), pfx, pc(test.buffer[k:])) + } + } +} + +func grave(n int) string { + return rep(0x0300, n) +} + +func rep(r rune, n int) string { + return strings.Repeat(string(r), n) +} + +const segSize = maxByteBufferSize + +var cgj = GraphemeJoiner + +var decomposeSegmentTests = []PositionTest{ + // illegal runes + {"\xC0", 0, ""}, + {"\u00E0\x80", 2, "\u0061\u0300"}, + // starter + {"a", 1, "a"}, + {"ab", 1, "a"}, + // starter + composing + {"a\u0300", 3, "a\u0300"}, + {"a\u0300b", 3, "a\u0300"}, + // with decomposition + {"\u00C0", 2, "A\u0300"}, + {"\u00C0b", 2, "A\u0300"}, + // long + {grave(31), 60, grave(30) + cgj}, + {"a" + grave(31), 61, "a" + grave(30) + cgj}, + + // Stability tests: see http://www.unicode.org/review/pr-29.html. + // U+0300 COMBINING GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING GRAVE;;;; + // U+0B47 ORIYA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; + // U+0B3E ORIYA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; + // U+1100 HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;;;; + // U+1161 HANGUL JUNGSEONG A;Lo;0;L;;;;;N;;;;; + {"\u0B47\u0300\u0B3E", 8, "\u0B47\u0300\u0B3E"}, + {"\u1100\u0300\u1161", 8, "\u1100\u0300\u1161"}, + {"\u0B47\u0B3E", 6, "\u0B47\u0B3E"}, + {"\u1100\u1161", 6, "\u1100\u1161"}, + + // U+04DA MALAYALAM VOWEL SIGN O;Mc;0;L;0D46 0D3E;;;;N;;;;; + // Sequence of decomposing characters that are starters and modifiers. + {"\u0d4a" + strings.Repeat("\u0d3e", 31), 90, "\u0d46" + strings.Repeat("\u0d3e", 30) + cgj}, + + {grave(30), 60, grave(30)}, + // U+FF9E is a starter, but decomposes to U+3099, which is not. + {grave(30) + "\uff9e", 60, grave(30) + cgj}, + // ends with incomplete UTF-8 encoding + {"\xCC", 0, ""}, + {"\u0300\xCC", 2, "\u0300"}, +} + +func decomposeSegmentF(rb *reorderBuffer, s string) (int, []byte) { + rb.initString(NFD, s) + rb.setFlusher(nil, appendFlush) + p := decomposeSegment(rb, 0, true) + return p, rb.out +} + +func TestDecomposeSegment(t *testing.T) { + runPosTests(t, "TestDecomposeSegment", NFC, decomposeSegmentF, decomposeSegmentTests) +} + +var firstBoundaryTests = []PositionTest{ + // no boundary + {"", -1, ""}, + {"\u0300", -1, ""}, + {"\x80\x80", -1, ""}, + // illegal runes + {"\xff", 0, ""}, + {"\u0300\xff", 2, ""}, + {"\u0300\xc0\x80\x80", 2, ""}, + // boundaries + {"a", 0, ""}, + {"\u0300a", 2, ""}, + // Hangul + {"\u1103\u1161", 0, ""}, + {"\u110B\u1173\u11B7", 0, ""}, + {"\u1161\u110B\u1173\u11B7", 3, ""}, + {"\u1173\u11B7\u1103\u1161", 6, ""}, + // too many combining characters. + {grave(maxNonStarters - 1), -1, ""}, + {grave(maxNonStarters), 60, ""}, + {grave(maxNonStarters + 1), 60, ""}, +} + +func firstBoundaryF(rb *reorderBuffer, s string) (int, []byte) { + return rb.f.form.FirstBoundary([]byte(s)), nil +} + +func firstBoundaryStringF(rb *reorderBuffer, s string) (int, []byte) { + return rb.f.form.FirstBoundaryInString(s), nil +} + +func TestFirstBoundary(t *testing.T) { + runPosTests(t, "TestFirstBoundary", NFC, firstBoundaryF, firstBoundaryTests) + runPosTests(t, "TestFirstBoundaryInString", NFC, firstBoundaryStringF, firstBoundaryTests) +} + +var decomposeToLastTests = []PositionTest{ + // ends with inert character + {"Hello!", 6, ""}, + {"\u0632", 2, ""}, + {"a\u0301\u0635", 5, ""}, + // ends with non-inert starter + {"a", 0, "a"}, + {"a\u0301a", 3, "a"}, + {"a\u0301\u03B9", 3, "\u03B9"}, + {"a\u0327", 0, "a\u0327"}, + // illegal runes + {"\xFF", 1, ""}, + {"aa\xFF", 3, ""}, + {"\xC0\x80\x80", 3, ""}, + {"\xCC\x80\x80", 3, ""}, + // ends with incomplete UTF-8 encoding + {"a\xCC", 2, ""}, + // ends with combining characters + {"\u0300\u0301", 0, "\u0300\u0301"}, + {"a\u0300\u0301", 0, "a\u0300\u0301"}, + {"a\u0301\u0308", 0, "a\u0301\u0308"}, + {"a\u0308\u0301", 0, "a\u0308\u0301"}, + {"aaaa\u0300\u0301", 3, "a\u0300\u0301"}, + {"\u0300a\u0300\u0301", 2, "a\u0300\u0301"}, + {"\u00C0", 0, "A\u0300"}, + {"a\u00C0", 1, "A\u0300"}, + // decomposing + {"a\u0300\u00E0", 3, "a\u0300"}, + // multisegment decompositions (flushes leading segments) + {"a\u0300\uFDC0", 7, "\u064A"}, + {"\uFDC0" + grave(29), 4, "\u064A" + grave(29)}, + {"\uFDC0" + grave(30), 4, "\u064A" + grave(30)}, + {"\uFDC0" + grave(31), 5, grave(30)}, + {"\uFDFA" + grave(14), 31, "\u0645" + grave(14)}, + // Overflow + {"\u00E0" + grave(29), 0, "a" + grave(30)}, + {"\u00E0" + grave(30), 2, grave(30)}, + // Hangul + {"a\u1103", 1, "\u1103"}, + {"a\u110B", 1, "\u110B"}, + {"a\u110B\u1173", 1, "\u110B\u1173"}, + // See comment in composition.go:compBoundaryAfter. + {"a\u110B\u1173\u11B7", 1, "\u110B\u1173\u11B7"}, + {"a\uC73C", 1, "\u110B\u1173"}, + {"다음", 3, "\u110B\u1173\u11B7"}, + {"다", 0, "\u1103\u1161"}, + {"\u1103\u1161\u110B\u1173\u11B7", 6, "\u110B\u1173\u11B7"}, + {"\u110B\u1173\u11B7\u1103\u1161", 9, "\u1103\u1161"}, + {"다음음", 6, "\u110B\u1173\u11B7"}, + {"음다다", 6, "\u1103\u1161"}, + // maximized buffer + {"a" + grave(30), 0, "a" + grave(30)}, + // Buffer overflow + {"a" + grave(31), 3, grave(30)}, + // weird UTF-8 + {"a\u0300\u11B7", 0, "a\u0300\u11B7"}, +} + +func decomposeToLast(rb *reorderBuffer, s string) (int, []byte) { + rb.setFlusher([]byte(s), appendFlush) + decomposeToLastBoundary(rb) + buf := rb.flush(nil) + return len(rb.out), buf +} + +func TestDecomposeToLastBoundary(t *testing.T) { + runPosTests(t, "TestDecomposeToLastBoundary", NFKC, decomposeToLast, decomposeToLastTests) +} + +var lastBoundaryTests = []PositionTest{ + // ends with inert character + {"Hello!", 6, ""}, + {"\u0632", 2, ""}, + // ends with non-inert starter + {"a", 0, ""}, + // illegal runes + {"\xff", 1, ""}, + {"aa\xff", 3, ""}, + {"a\xff\u0300", 1, ""}, + {"\xc0\x80\x80", 3, ""}, + {"\xc0\x80\x80\u0300", 3, ""}, + // ends with incomplete UTF-8 encoding + {"\xCC", -1, ""}, + {"\xE0\x80", -1, ""}, + {"\xF0\x80\x80", -1, ""}, + {"a\xCC", 0, ""}, + {"\x80\xCC", 1, ""}, + {"\xCC\xCC", 1, ""}, + // ends with combining characters + {"a\u0300\u0301", 0, ""}, + {"aaaa\u0300\u0301", 3, ""}, + {"\u0300a\u0300\u0301", 2, ""}, + {"\u00C0", 0, ""}, + {"a\u00C0", 1, ""}, + // decomposition may recombine + {"\u0226", 0, ""}, + // no boundary + {"", -1, ""}, + {"\u0300\u0301", -1, ""}, + {"\u0300", -1, ""}, + {"\x80\x80", -1, ""}, + {"\x80\x80\u0301", -1, ""}, + // Hangul + {"다음", 3, ""}, + {"다", 0, ""}, + {"\u1103\u1161\u110B\u1173\u11B7", 6, ""}, + {"\u110B\u1173\u11B7\u1103\u1161", 9, ""}, + // too many combining characters. + {grave(maxNonStarters - 1), -1, ""}, + // May still be preceded with a non-starter. + {grave(maxNonStarters), -1, ""}, + // May still need to insert a cgj after the last combiner. + {grave(maxNonStarters + 1), 2, ""}, + {grave(maxNonStarters + 2), 4, ""}, + + {"a" + grave(maxNonStarters-1), 0, ""}, + {"a" + grave(maxNonStarters), 0, ""}, + // May still need to insert a cgj after the last combiner. + {"a" + grave(maxNonStarters+1), 3, ""}, + {"a" + grave(maxNonStarters+2), 5, ""}, +} + +func lastBoundaryF(rb *reorderBuffer, s string) (int, []byte) { + return rb.f.form.LastBoundary([]byte(s)), nil +} + +func TestLastBoundary(t *testing.T) { + runPosTests(t, "TestLastBoundary", NFC, lastBoundaryF, lastBoundaryTests) +} + +var quickSpanTests = []PositionTest{ + {"", 0, ""}, + // starters + {"a", 1, ""}, + {"abc", 3, ""}, + {"\u043Eb", 3, ""}, + // incomplete last rune. + {"\xCC", 1, ""}, + {"a\xCC", 2, ""}, + // incorrectly ordered combining characters + {"\u0300\u0316", 0, ""}, + {"\u0300\u0316cd", 0, ""}, + // have a maximum number of combining characters. + {rep(0x035D, 30) + "\u035B", 0, ""}, + {"a" + rep(0x035D, 30) + "\u035B", 0, ""}, + {"Ɵ" + rep(0x035D, 30) + "\u035B", 0, ""}, + {"aa" + rep(0x035D, 30) + "\u035B", 1, ""}, + {rep(0x035D, 30) + cgj + "\u035B", 64, ""}, + {"a" + rep(0x035D, 30) + cgj + "\u035B", 65, ""}, + {"Ɵ" + rep(0x035D, 30) + cgj + "\u035B", 66, ""}, + {"aa" + rep(0x035D, 30) + cgj + "\u035B", 66, ""}, +} + +var quickSpanNFDTests = []PositionTest{ + // needs decomposing + {"\u00C0", 0, ""}, + {"abc\u00C0", 3, ""}, + // correctly ordered combining characters + {"\u0300", 2, ""}, + {"ab\u0300", 4, ""}, + {"ab\u0300cd", 6, ""}, + {"\u0300cd", 4, ""}, + {"\u0316\u0300", 4, ""}, + {"ab\u0316\u0300", 6, ""}, + {"ab\u0316\u0300cd", 8, ""}, + {"ab\u0316\u0300\u00C0", 6, ""}, + {"\u0316\u0300cd", 6, ""}, + {"\u043E\u0308b", 5, ""}, + // incorrectly ordered combining characters + {"ab\u0300\u0316", 1, ""}, // TODO: we could skip 'b' as well. + {"ab\u0300\u0316cd", 1, ""}, + // Hangul + {"같은", 0, ""}, +} + +var quickSpanNFCTests = []PositionTest{ + // okay composed + {"\u00C0", 2, ""}, + {"abc\u00C0", 5, ""}, + // correctly ordered combining characters + {"ab\u0300", 1, ""}, + {"ab\u0300cd", 1, ""}, + {"ab\u0316\u0300", 1, ""}, + {"ab\u0316\u0300cd", 1, ""}, + {"\u00C0\u035D", 4, ""}, + // we do not special case leading combining characters + {"\u0300cd", 0, ""}, + {"\u0300", 0, ""}, + {"\u0316\u0300", 0, ""}, + {"\u0316\u0300cd", 0, ""}, + // incorrectly ordered combining characters + {"ab\u0300\u0316", 1, ""}, + {"ab\u0300\u0316cd", 1, ""}, + // Hangul + {"같은", 6, ""}, + // We return the start of the violating segment in case of overflow. + {grave(30) + "\uff9e", 0, ""}, + {grave(30), 0, ""}, +} + +func doQuickSpan(rb *reorderBuffer, s string) (int, []byte) { + return rb.f.form.QuickSpan([]byte(s)), nil +} + +func doQuickSpanString(rb *reorderBuffer, s string) (int, []byte) { + return rb.f.form.QuickSpanString(s), nil +} + +func TestQuickSpan(t *testing.T) { + runPosTests(t, "TestQuickSpanNFD1", NFD, doQuickSpan, quickSpanTests) + runPosTests(t, "TestQuickSpanNFD2", NFD, doQuickSpan, quickSpanNFDTests) + runPosTests(t, "TestQuickSpanNFC1", NFC, doQuickSpan, quickSpanTests) + runPosTests(t, "TestQuickSpanNFC2", NFC, doQuickSpan, quickSpanNFCTests) + + runPosTests(t, "TestQuickSpanStringNFD1", NFD, doQuickSpanString, quickSpanTests) + runPosTests(t, "TestQuickSpanStringNFD2", NFD, doQuickSpanString, quickSpanNFDTests) + runPosTests(t, "TestQuickSpanStringNFC1", NFC, doQuickSpanString, quickSpanTests) + runPosTests(t, "TestQuickSpanStringNFC2", NFC, doQuickSpanString, quickSpanNFCTests) +} + +var isNormalTests = []PositionTest{ + {"", 1, ""}, + // illegal runes + {"\xff", 1, ""}, + // starters + {"a", 1, ""}, + {"abc", 1, ""}, + {"\u043Eb", 1, ""}, + // incorrectly ordered combining characters + {"\u0300\u0316", 0, ""}, + {"ab\u0300\u0316", 0, ""}, + {"ab\u0300\u0316cd", 0, ""}, + {"\u0300\u0316cd", 0, ""}, +} +var isNormalNFDTests = []PositionTest{ + // needs decomposing + {"\u00C0", 0, ""}, + {"abc\u00C0", 0, ""}, + // correctly ordered combining characters + {"\u0300", 1, ""}, + {"ab\u0300", 1, ""}, + {"ab\u0300cd", 1, ""}, + {"\u0300cd", 1, ""}, + {"\u0316\u0300", 1, ""}, + {"ab\u0316\u0300", 1, ""}, + {"ab\u0316\u0300cd", 1, ""}, + {"\u0316\u0300cd", 1, ""}, + {"\u043E\u0308b", 1, ""}, + // Hangul + {"같은", 0, ""}, +} +var isNormalNFCTests = []PositionTest{ + // okay composed + {"\u00C0", 1, ""}, + {"abc\u00C0", 1, ""}, + // need reordering + {"a\u0300", 0, ""}, + {"a\u0300cd", 0, ""}, + {"a\u0316\u0300", 0, ""}, + {"a\u0316\u0300cd", 0, ""}, + // correctly ordered combining characters + {"ab\u0300", 1, ""}, + {"ab\u0300cd", 1, ""}, + {"ab\u0316\u0300", 1, ""}, + {"ab\u0316\u0300cd", 1, ""}, + {"\u00C0\u035D", 1, ""}, + {"\u0300", 1, ""}, + {"\u0316\u0300cd", 1, ""}, + // Hangul + {"같은", 1, ""}, +} + +var isNormalNFKXTests = []PositionTest{ + // Special case. + {"\u00BC", 0, ""}, +} + +func isNormalF(rb *reorderBuffer, s string) (int, []byte) { + if rb.f.form.IsNormal([]byte(s)) { + return 1, nil + } + return 0, nil +} + +func isNormalStringF(rb *reorderBuffer, s string) (int, []byte) { + if rb.f.form.IsNormalString(s) { + return 1, nil + } + return 0, nil +} + +func TestIsNormal(t *testing.T) { + runPosTests(t, "TestIsNormalNFD1", NFD, isNormalF, isNormalTests) + runPosTests(t, "TestIsNormalNFD2", NFD, isNormalF, isNormalNFDTests) + runPosTests(t, "TestIsNormalNFC1", NFC, isNormalF, isNormalTests) + runPosTests(t, "TestIsNormalNFC2", NFC, isNormalF, isNormalNFCTests) + runPosTests(t, "TestIsNormalNFKD1", NFKD, isNormalF, isNormalTests) + runPosTests(t, "TestIsNormalNFKD2", NFKD, isNormalF, isNormalNFDTests) + runPosTests(t, "TestIsNormalNFKD3", NFKD, isNormalF, isNormalNFKXTests) + runPosTests(t, "TestIsNormalNFKC1", NFKC, isNormalF, isNormalTests) + runPosTests(t, "TestIsNormalNFKC2", NFKC, isNormalF, isNormalNFCTests) + runPosTests(t, "TestIsNormalNFKC3", NFKC, isNormalF, isNormalNFKXTests) +} + +func TestIsNormalString(t *testing.T) { + runPosTests(t, "TestIsNormalNFD1", NFD, isNormalStringF, isNormalTests) + runPosTests(t, "TestIsNormalNFD2", NFD, isNormalStringF, isNormalNFDTests) + runPosTests(t, "TestIsNormalNFC1", NFC, isNormalStringF, isNormalTests) + runPosTests(t, "TestIsNormalNFC2", NFC, isNormalStringF, isNormalNFCTests) +} + +type AppendTest struct { + left string + right string + out string +} + +type appendFunc func(f Form, out []byte, s string) []byte + +var fstr = []string{"NFC", "NFD", "NFKC", "NFKD"} + +func runNormTests(t *testing.T, name string, fn appendFunc) { + for f := NFC; f <= NFKD; f++ { + runAppendTests(t, name, f, fn, normTests[f]) + } +} + +func runAppendTests(t *testing.T, name string, f Form, fn appendFunc, tests []AppendTest) { + for i, test := range tests { + if *testn >= 0 && i != *testn { + continue + } + out := []byte(test.left) + have := string(fn(f, out, test.right)) + if len(have) != len(test.out) { + t.Errorf("%s.%s:%d: length is %d; want %d (%+q vs %+q)", fstr[f], name, i, len(have), len(test.out), pc(have), pc(test.out)) + } + if have != test.out { + k, pf := pidx(have, test.out) + t.Errorf("%s.%s:%d: \nwas %s%+q; \nwant %s%+q", fstr[f], name, i, pf, pc(have[k:]), pf, pc(test.out[k:])) + } + + // Bootstrap by normalizing input. Ensures that the various variants + // behave the same. + for g := NFC; g <= NFKD; g++ { + if f == g { + continue + } + want := g.String(test.left + test.right) + have := string(fn(g, g.AppendString(nil, test.left), test.right)) + if len(have) != len(want) { + t.Errorf("%s(%s.%s):%d: length is %d; want %d (%+q vs %+q)", fstr[g], fstr[f], name, i, len(have), len(want), pc(have), pc(want)) + } + if have != want { + k, pf := pidx(have, want) + t.Errorf("%s(%s.%s):%d: \nwas %s%+q; \nwant %s%+q", fstr[g], fstr[f], name, i, pf, pc(have[k:]), pf, pc(want[k:])) + } + } + } +} + +var normTests = [][]AppendTest{ + appendTestsNFC, + appendTestsNFD, + appendTestsNFKC, + appendTestsNFKD, +} + +var appendTestsNFC = []AppendTest{ + {"", ascii, ascii}, + {"", txt_all, txt_all}, + {"\uff9e", grave(30), "\uff9e" + grave(29) + cgj + grave(1)}, + {grave(30), "\uff9e", grave(30) + cgj + "\uff9e"}, + + // Tests designed for Iter. + { // ordering of non-composing combining characters + "", + "\u0305\u0316", + "\u0316\u0305", + }, + { // segment overflow + "", + "a" + rep(0x0305, maxNonStarters+4) + "\u0316", + "a" + rep(0x0305, maxNonStarters) + cgj + "\u0316" + rep(0x305, 4), + }, + + { // Combine across non-blocking non-starters. + // U+0327 COMBINING CEDILLA;Mn;202;NSM;;;;;N;NON-SPACING CEDILLA;;;; + // U+0325 COMBINING RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RING BELOW;;;; + "", "a\u0327\u0325", "\u1e01\u0327", + }, + + { // Jamo V+T does not combine. + "", + "\u1161\u11a8", + "\u1161\u11a8", + }, + + // Stability tests: see http://www.unicode.org/review/pr-29.html. + {"", "\u0b47\u0300\u0b3e", "\u0b47\u0300\u0b3e"}, + {"", "\u1100\u0300\u1161", "\u1100\u0300\u1161"}, + {"", "\u0b47\u0b3e", "\u0b4b"}, + {"", "\u1100\u1161", "\uac00"}, + + // U+04DA MALAYALAM VOWEL SIGN O;Mc;0;L;0D46 0D3E;;;;N;;;;; + { // 0d4a starts a new segment. + "", + "\u0d4a" + strings.Repeat("\u0d3e", 15) + "\u0d4a" + strings.Repeat("\u0d3e", 15), + "\u0d4a" + strings.Repeat("\u0d3e", 15) + "\u0d4a" + strings.Repeat("\u0d3e", 15), + }, + + { // Split combining characters. + // TODO: don't insert CGJ before starters. + "", + "\u0d46" + strings.Repeat("\u0d3e", 31), + "\u0d4a" + strings.Repeat("\u0d3e", 29) + cgj + "\u0d3e", + }, + + { // Split combining characters. + "", + "\u0d4a" + strings.Repeat("\u0d3e", 30), + "\u0d4a" + strings.Repeat("\u0d3e", 29) + cgj + "\u0d3e", + }, +} + +var appendTestsNFD = []AppendTest{ +// TODO: Move some of the tests here. +} + +var appendTestsNFKC = []AppendTest{ + // empty buffers + {"", "", ""}, + {"a", "", "a"}, + {"", "a", "a"}, + {"", "\u0041\u0307\u0304", "\u01E0"}, + // segment split across buffers + {"", "a\u0300b", "\u00E0b"}, + {"a", "\u0300b", "\u00E0b"}, + {"a", "\u0300\u0316", "\u00E0\u0316"}, + {"a", "\u0316\u0300", "\u00E0\u0316"}, + {"a", "\u0300a\u0300", "\u00E0\u00E0"}, + {"a", "\u0300a\u0300a\u0300", "\u00E0\u00E0\u00E0"}, + {"a", "\u0300aaa\u0300aaa\u0300", "\u00E0aa\u00E0aa\u00E0"}, + {"a\u0300", "\u0327", "\u00E0\u0327"}, + {"a\u0327", "\u0300", "\u00E0\u0327"}, + {"a\u0316", "\u0300", "\u00E0\u0316"}, + {"\u0041\u0307", "\u0304", "\u01E0"}, + // Hangul + {"", "\u110B\u1173", "\uC73C"}, + {"", "\u1103\u1161", "\uB2E4"}, + {"", "\u110B\u1173\u11B7", "\uC74C"}, + {"", "\u320E", "\x28\uAC00\x29"}, + {"", "\x28\u1100\u1161\x29", "\x28\uAC00\x29"}, + {"\u1103", "\u1161", "\uB2E4"}, + {"\u110B", "\u1173\u11B7", "\uC74C"}, + {"\u110B\u1173", "\u11B7", "\uC74C"}, + {"\uC73C", "\u11B7", "\uC74C"}, + // UTF-8 encoding split across buffers + {"a\xCC", "\x80", "\u00E0"}, + {"a\xCC", "\x80b", "\u00E0b"}, + {"a\xCC", "\x80a\u0300", "\u00E0\u00E0"}, + {"a\xCC", "\x80\x80", "\u00E0\x80"}, + {"a\xCC", "\x80\xCC", "\u00E0\xCC"}, + {"a\u0316\xCC", "\x80a\u0316\u0300", "\u00E0\u0316\u00E0\u0316"}, + // ending in incomplete UTF-8 encoding + {"", "\xCC", "\xCC"}, + {"a", "\xCC", "a\xCC"}, + {"a", "b\xCC", "ab\xCC"}, + {"\u0226", "\xCC", "\u0226\xCC"}, + // illegal runes + {"", "\x80", "\x80"}, + {"", "\x80\x80\x80", "\x80\x80\x80"}, + {"", "\xCC\x80\x80\x80", "\xCC\x80\x80\x80"}, + {"", "a\x80", "a\x80"}, + {"", "a\x80\x80\x80", "a\x80\x80\x80"}, + {"", "a\x80\x80\x80\x80\x80\x80", "a\x80\x80\x80\x80\x80\x80"}, + {"a", "\x80\x80\x80", "a\x80\x80\x80"}, + // overflow + {"", strings.Repeat("\x80", 33), strings.Repeat("\x80", 33)}, + {strings.Repeat("\x80", 33), "", strings.Repeat("\x80", 33)}, + {strings.Repeat("\x80", 33), strings.Repeat("\x80", 33), strings.Repeat("\x80", 66)}, + // overflow of combining characters + {"", grave(34), grave(30) + cgj + grave(4)}, + {"", grave(36), grave(30) + cgj + grave(6)}, + {grave(29), grave(5), grave(30) + cgj + grave(4)}, + {grave(30), grave(4), grave(30) + cgj + grave(4)}, + {grave(30), grave(3), grave(30) + cgj + grave(3)}, + {grave(30) + "\xCC", "\x80", grave(30) + cgj + grave(1)}, + {"", "\uFDFA" + grave(14), "\u0635\u0644\u0649 \u0627\u0644\u0644\u0647 \u0639\u0644\u064a\u0647 \u0648\u0633\u0644\u0645" + grave(14)}, + {"", "\uFDFA" + grave(28) + "\u0316", "\u0635\u0644\u0649 \u0627\u0644\u0644\u0647 \u0639\u0644\u064a\u0647 \u0648\u0633\u0644\u0645\u0316" + grave(28)}, + // - First rune has a trailing non-starter. + {"\u00d5", grave(30), "\u00d5" + grave(29) + cgj + grave(1)}, + // - U+FF9E decomposes into a non-starter in compatibility mode. A CGJ must be + // inserted even when FF9E starts a new segment. + {"\uff9e", grave(30), "\u3099" + grave(29) + cgj + grave(1)}, + {grave(30), "\uff9e", grave(30) + cgj + "\u3099"}, + // - Many non-starter decompositions in a row causing overflow. + {"", rep(0x340, 31), rep(0x300, 30) + cgj + "\u0300"}, + {"", rep(0xFF9E, 31), rep(0x3099, 30) + cgj + "\u3099"}, + // weird UTF-8 + {"\u00E0\xE1", "\x86", "\u00E0\xE1\x86"}, + {"a\u0300\u11B7", "\u0300", "\u00E0\u11B7\u0300"}, + {"a\u0300\u11B7\u0300", "\u0300", "\u00E0\u11B7\u0300\u0300"}, + {"\u0300", "\xF8\x80\x80\x80\x80\u0300", "\u0300\xF8\x80\x80\x80\x80\u0300"}, + {"\u0300", "\xFC\x80\x80\x80\x80\x80\u0300", "\u0300\xFC\x80\x80\x80\x80\x80\u0300"}, + {"\xF8\x80\x80\x80\x80\u0300", "\u0300", "\xF8\x80\x80\x80\x80\u0300\u0300"}, + {"\xFC\x80\x80\x80\x80\x80\u0300", "\u0300", "\xFC\x80\x80\x80\x80\x80\u0300\u0300"}, + {"\xF8\x80\x80\x80", "\x80\u0300\u0300", "\xF8\x80\x80\x80\x80\u0300\u0300"}, + + {"", strings.Repeat("a\u0316\u0300", 6), strings.Repeat("\u00E0\u0316", 6)}, + // large input. + {"", strings.Repeat("a\u0300\u0316", 4000), strings.Repeat("\u00E0\u0316", 4000)}, + {"", strings.Repeat("\x80\x80", 4000), strings.Repeat("\x80\x80", 4000)}, + {"", "\u0041\u0307\u0304", "\u01E0"}, +} + +var appendTestsNFKD = []AppendTest{ + {"", "a" + grave(64), "a" + grave(30) + cgj + grave(30) + cgj + grave(4)}, + + { // segment overflow on unchanged character + "", + "a" + grave(64) + "\u0316", + "a" + grave(30) + cgj + grave(30) + cgj + "\u0316" + grave(4), + }, + { // segment overflow on unchanged character + start value + "", + "a" + grave(98) + "\u0316", + "a" + grave(30) + cgj + grave(30) + cgj + grave(30) + cgj + "\u0316" + grave(8), + }, + { // segment overflow on decomposition. (U+0340 decomposes to U+0300.) + "", + "a" + grave(59) + "\u0340", + "a" + grave(30) + cgj + grave(30), + }, + { // segment overflow on non-starter decomposition + "", + "a" + grave(33) + "\u0340" + grave(30) + "\u0320", + "a" + grave(30) + cgj + grave(30) + cgj + "\u0320" + grave(4), + }, + { // start value after ASCII overflow + "", + rep('a', segSize) + grave(32) + "\u0320", + rep('a', segSize) + grave(30) + cgj + "\u0320" + grave(2), + }, + { // Jamo overflow + "", + "\u1100\u1161" + grave(30) + "\u0320" + grave(2), + "\u1100\u1161" + grave(29) + cgj + "\u0320" + grave(3), + }, + { // Hangul + "", + "\uac00", + "\u1100\u1161", + }, + { // Hangul overflow + "", + "\uac00" + grave(32) + "\u0320", + "\u1100\u1161" + grave(29) + cgj + "\u0320" + grave(3), + }, + { // Hangul overflow in Hangul mode. + "", + "\uac00\uac00" + grave(32) + "\u0320", + "\u1100\u1161\u1100\u1161" + grave(29) + cgj + "\u0320" + grave(3), + }, + { // Hangul overflow in Hangul mode. + "", + strings.Repeat("\uac00", 3) + grave(32) + "\u0320", + strings.Repeat("\u1100\u1161", 3) + grave(29) + cgj + "\u0320" + grave(3), + }, + { // start value after cc=0 + "", + "您您" + grave(34) + "\u0320", + "您您" + grave(30) + cgj + "\u0320" + grave(4), + }, + { // start value after normalization + "", + "\u0300\u0320a" + grave(34) + "\u0320", + "\u0320\u0300a" + grave(30) + cgj + "\u0320" + grave(4), + }, +} + +func TestAppend(t *testing.T) { + runNormTests(t, "Append", func(f Form, out []byte, s string) []byte { + return f.Append(out, []byte(s)...) + }) +} + +func TestAppendString(t *testing.T) { + runNormTests(t, "AppendString", func(f Form, out []byte, s string) []byte { + return f.AppendString(out, s) + }) +} + +func TestBytes(t *testing.T) { + runNormTests(t, "Bytes", func(f Form, out []byte, s string) []byte { + buf := []byte{} + buf = append(buf, out...) + buf = append(buf, s...) + return f.Bytes(buf) + }) +} + +func TestString(t *testing.T) { + runNormTests(t, "String", func(f Form, out []byte, s string) []byte { + outs := string(out) + s + return []byte(f.String(outs)) + }) +} + +func appendBench(f Form, in []byte) func() { + buf := make([]byte, 0, 4*len(in)) + return func() { + f.Append(buf, in...) + } +} + +func bytesBench(f Form, in []byte) func() { + return func() { + f.Bytes(in) + } +} + +func iterBench(f Form, in []byte) func() { + iter := Iter{} + return func() { + iter.Init(f, in) + for !iter.Done() { + iter.Next() + } + } +} + +func transformBench(f Form, in []byte) func() { + buf := make([]byte, 4*len(in)) + return func() { + if _, n, err := f.Transform(buf, in, true); err != nil || len(in) != n { + log.Panic(n, len(in), err) + } + } +} + +func readerBench(f Form, in []byte) func() { + buf := make([]byte, 4*len(in)) + return func() { + r := f.Reader(bytes.NewReader(in)) + var err error + for err == nil { + _, err = r.Read(buf) + } + if err != io.EOF { + panic("") + } + } +} + +func writerBench(f Form, in []byte) func() { + buf := make([]byte, 0, 4*len(in)) + return func() { + r := f.Writer(bytes.NewBuffer(buf)) + if _, err := r.Write(in); err != nil { + panic("") + } + } +} + +func appendBenchmarks(bm []func(), f Form, in []byte) []func() { + bm = append(bm, appendBench(f, in)) + bm = append(bm, iterBench(f, in)) + bm = append(bm, transformBench(f, in)) + bm = append(bm, readerBench(f, in)) + bm = append(bm, writerBench(f, in)) + return bm +} + +func doFormBenchmark(b *testing.B, inf, f Form, s string) { + b.StopTimer() + in := inf.Bytes([]byte(s)) + bm := appendBenchmarks(nil, f, in) + b.SetBytes(int64(len(in) * len(bm))) + b.StartTimer() + for i := 0; i < b.N; i++ { + for _, fn := range bm { + fn() + } + } +} + +func doSingle(b *testing.B, f func(Form, []byte) func(), s []byte) { + b.StopTimer() + fn := f(NFC, s) + b.SetBytes(int64(len(s))) + b.StartTimer() + for i := 0; i < b.N; i++ { + fn() + } +} + +var ( + smallNoChange = []byte("nörmalization") + smallChange = []byte("No\u0308rmalization") + ascii = strings.Repeat("There is nothing to change here! ", 500) +) + +func lowerBench(f Form, in []byte) func() { + // Use package strings instead of bytes as it doesn't allocate memory + // if there aren't any changes. + s := string(in) + return func() { + strings.ToLower(s) + } +} + +func BenchmarkLowerCaseNoChange(b *testing.B) { + doSingle(b, lowerBench, smallNoChange) +} +func BenchmarkLowerCaseChange(b *testing.B) { + doSingle(b, lowerBench, smallChange) +} + +func quickSpanBench(f Form, in []byte) func() { + return func() { + f.QuickSpan(in) + } +} + +func BenchmarkQuickSpanChangeNFC(b *testing.B) { + doSingle(b, quickSpanBench, smallNoChange) +} + +func BenchmarkBytesNoChangeNFC(b *testing.B) { + doSingle(b, bytesBench, smallNoChange) +} +func BenchmarkBytesChangeNFC(b *testing.B) { + doSingle(b, bytesBench, smallChange) +} + +func BenchmarkAppendNoChangeNFC(b *testing.B) { + doSingle(b, appendBench, smallNoChange) +} +func BenchmarkAppendChangeNFC(b *testing.B) { + doSingle(b, appendBench, smallChange) +} +func BenchmarkAppendLargeNFC(b *testing.B) { + doSingle(b, appendBench, txt_all_bytes) +} + +func BenchmarkIterNoChangeNFC(b *testing.B) { + doSingle(b, iterBench, smallNoChange) +} +func BenchmarkIterChangeNFC(b *testing.B) { + doSingle(b, iterBench, smallChange) +} +func BenchmarkIterLargeNFC(b *testing.B) { + doSingle(b, iterBench, txt_all_bytes) +} + +func BenchmarkTransformNoChangeNFC(b *testing.B) { + doSingle(b, transformBench, smallNoChange) +} +func BenchmarkTransformChangeNFC(b *testing.B) { + doSingle(b, transformBench, smallChange) +} +func BenchmarkTransformLargeNFC(b *testing.B) { + doSingle(b, transformBench, txt_all_bytes) +} + +func BenchmarkNormalizeAsciiNFC(b *testing.B) { + doFormBenchmark(b, NFC, NFC, ascii) +} +func BenchmarkNormalizeAsciiNFD(b *testing.B) { + doFormBenchmark(b, NFC, NFD, ascii) +} +func BenchmarkNormalizeAsciiNFKC(b *testing.B) { + doFormBenchmark(b, NFC, NFKC, ascii) +} +func BenchmarkNormalizeAsciiNFKD(b *testing.B) { + doFormBenchmark(b, NFC, NFKD, ascii) +} + +func BenchmarkNormalizeNFC2NFC(b *testing.B) { + doFormBenchmark(b, NFC, NFC, txt_all) +} +func BenchmarkNormalizeNFC2NFD(b *testing.B) { + doFormBenchmark(b, NFC, NFD, txt_all) +} +func BenchmarkNormalizeNFD2NFC(b *testing.B) { + doFormBenchmark(b, NFD, NFC, txt_all) +} +func BenchmarkNormalizeNFD2NFD(b *testing.B) { + doFormBenchmark(b, NFD, NFD, txt_all) +} + +// Hangul is often special-cased, so we test it separately. +func BenchmarkNormalizeHangulNFC2NFC(b *testing.B) { + doFormBenchmark(b, NFC, NFC, txt_kr) +} +func BenchmarkNormalizeHangulNFC2NFD(b *testing.B) { + doFormBenchmark(b, NFC, NFD, txt_kr) +} +func BenchmarkNormalizeHangulNFD2NFC(b *testing.B) { + doFormBenchmark(b, NFD, NFC, txt_kr) +} +func BenchmarkNormalizeHangulNFD2NFD(b *testing.B) { + doFormBenchmark(b, NFD, NFD, txt_kr) +} + +var forms = []Form{NFC, NFD, NFKC, NFKD} + +func doTextBenchmark(b *testing.B, s string) { + b.StopTimer() + in := []byte(s) + bm := []func(){} + for _, f := range forms { + bm = appendBenchmarks(bm, f, in) + } + b.SetBytes(int64(len(s) * len(bm))) + b.StartTimer() + for i := 0; i < b.N; i++ { + for _, f := range bm { + f() + } + } +} + +func BenchmarkCanonicalOrdering(b *testing.B) { + doTextBenchmark(b, txt_canon) +} +func BenchmarkExtendedLatin(b *testing.B) { + doTextBenchmark(b, txt_vn) +} +func BenchmarkMiscTwoByteUtf8(b *testing.B) { + doTextBenchmark(b, twoByteUtf8) +} +func BenchmarkMiscThreeByteUtf8(b *testing.B) { + doTextBenchmark(b, threeByteUtf8) +} +func BenchmarkHangul(b *testing.B) { + doTextBenchmark(b, txt_kr) +} +func BenchmarkJapanese(b *testing.B) { + doTextBenchmark(b, txt_jp) +} +func BenchmarkChinese(b *testing.B) { + doTextBenchmark(b, txt_cn) +} +func BenchmarkOverflow(b *testing.B) { + doTextBenchmark(b, overflow) +} + +var overflow = string(bytes.Repeat([]byte("\u035D"), 4096)) + "\u035B" + +// Tests sampled from the Canonical ordering tests (Part 2) of +// http://unicode.org/Public/UNIDATA/NormalizationTest.txt +const txt_canon = `\u0061\u0315\u0300\u05AE\u0300\u0062 \u0061\u0300\u0315\u0300\u05AE\u0062 +\u0061\u0302\u0315\u0300\u05AE\u0062 \u0061\u0307\u0315\u0300\u05AE\u0062 +\u0061\u0315\u0300\u05AE\u030A\u0062 \u0061\u059A\u0316\u302A\u031C\u0062 +\u0061\u032E\u059A\u0316\u302A\u0062 \u0061\u0338\u093C\u0334\u0062 +\u0061\u059A\u0316\u302A\u0339 \u0061\u0341\u0315\u0300\u05AE\u0062 +\u0061\u0348\u059A\u0316\u302A\u0062 \u0061\u0361\u0345\u035D\u035C\u0062 +\u0061\u0366\u0315\u0300\u05AE\u0062 \u0061\u0315\u0300\u05AE\u0486\u0062 +\u0061\u05A4\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0613\u0062 +\u0061\u0315\u0300\u05AE\u0615\u0062 \u0061\u0617\u0315\u0300\u05AE\u0062 +\u0061\u0619\u0618\u064D\u064E\u0062 \u0061\u0315\u0300\u05AE\u0654\u0062 +\u0061\u0315\u0300\u05AE\u06DC\u0062 \u0061\u0733\u0315\u0300\u05AE\u0062 +\u0061\u0744\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0745\u0062 +\u0061\u09CD\u05B0\u094D\u3099\u0062 \u0061\u0E38\u0E48\u0E38\u0C56\u0062 +\u0061\u0EB8\u0E48\u0E38\u0E49\u0062 \u0061\u0F72\u0F71\u0EC8\u0F71\u0062 +\u0061\u1039\u05B0\u094D\u3099\u0062 \u0061\u05B0\u094D\u3099\u1A60\u0062 +\u0061\u3099\u093C\u0334\u1BE6\u0062 \u0061\u3099\u093C\u0334\u1C37\u0062 +\u0061\u1CD9\u059A\u0316\u302A\u0062 \u0061\u2DED\u0315\u0300\u05AE\u0062 +\u0061\u2DEF\u0315\u0300\u05AE\u0062 \u0061\u302D\u302E\u059A\u0316\u0062` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/vn/ +const txt_vn = `Với các điều kiện sau: Ghi nhận công của tác giả. +Nếu bạn sử dụng, chuyển đổi, hoặc xây dựng dự án từ +nội dung được chia sẻ này, bạn phải áp dụng giấy phép này hoặc +một giấy phép khác có các điều khoản tương tự như giấy phép này +cho dự án của bạn. Hiểu rằng: Miễn — Bất kỳ các điều kiện nào +trên đây cũng có thể được miễn bỏ nếu bạn được sự cho phép của +người sở hữu bản quyền. Phạm vi công chúng — Khi tác phẩm hoặc +bất kỳ chương nào của tác phẩm đã trong vùng dành cho công +chúng theo quy định của pháp luật thì tình trạng của nó không +bị ảnh hưởng bởi giấy phép trong bất kỳ trường hợp nào.` + +// Taken from http://creativecommons.org/licenses/by-sa/1.0/deed.ru +const txt_ru = `При обязательном соблюдении следующих условий: +Attribution — Вы должны атрибутировать произведение (указывать +автора и источник) в порядке, предусмотренном автором или +лицензиаром (но только так, чтобы никоим образом не подразумевалось, +что они поддерживают вас или использование вами данного произведения). +Υπό τις ακόλουθες προϋποθέσεις:` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/gr/ +const txt_gr = `Αναφορά Δημιουργού — Θα πρέπει να κάνετε την αναφορά στο έργο με τον +τρόπο που έχει οριστεί από το δημιουργό ή το χορηγούντο την άδεια +(χωρίς όμως να εννοείται με οποιονδήποτε τρόπο ότι εγκρίνουν εσάς ή +τη χρήση του έργου από εσάς). Παρόμοια Διανομή — Εάν αλλοιώσετε, +τροποποιήσετε ή δημιουργήσετε περαιτέρω βασισμένοι στο έργο θα +μπορείτε να διανέμετε το έργο που θα προκύψει μόνο με την ίδια ή +παρόμοια άδεια.` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/deed.ar +const txt_ar = `بموجب الشروط التالية نسب المصنف — يجب عليك أن +تنسب العمل بالطريقة التي تحددها المؤلف أو المرخص (ولكن ليس بأي حال من +الأحوال أن توحي وتقترح بتحول أو استخدامك للعمل). +المشاركة على قدم المساواة — إذا كنت يعدل ، والتغيير ، أو الاستفادة +من هذا العمل ، قد ينتج عن توزيع العمل إلا في ظل تشابه او تطابق فى واحد +لهذا الترخيص.` + +// Taken from http://creativecommons.org/licenses/by-sa/1.0/il/ +const txt_il = `בכפוף לתנאים הבאים: ייחוס — עליך לייחס את היצירה (לתת קרדיט) באופן +המצויין על-ידי היוצר או מעניק הרישיון (אך לא בשום אופן המרמז על כך +שהם תומכים בך או בשימוש שלך ביצירה). שיתוף זהה — אם תחליט/י לשנות, +לעבד או ליצור יצירה נגזרת בהסתמך על יצירה זו, תוכל/י להפיץ את יצירתך +החדשה רק תחת אותו הרישיון או רישיון דומה לרישיון זה.` + +const twoByteUtf8 = txt_ru + txt_gr + txt_ar + txt_il + +// Taken from http://creativecommons.org/licenses/by-sa/2.0/kr/ +const txt_kr = `다음과 같은 조건을 따라야 합니다: 저작자표시 +(Attribution) — 저작자나 이용허락자가 정한 방법으로 저작물의 +원저작자를 표시하여야 합니다(그러나 원저작자가 이용자나 이용자의 +이용을 보증하거나 추천한다는 의미로 표시해서는 안됩니다). +동일조건변경허락 — 이 저작물을 이용하여 만든 이차적 저작물에는 본 +라이선스와 동일한 라이선스를 적용해야 합니다.` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/th/ +const txt_th = `ภายใต้เงื่อนไข ดังต่อไปนี้ : แสดงที่มา — คุณต้องแสดงที่ +มาของงานดังกล่าว ตามรูปแบบที่ผู้สร้างสรรค์หรือผู้อนุญาตกำหนด (แต่ +ไม่ใช่ในลักษณะที่ว่า พวกเขาสนับสนุนคุณหรือสนับสนุนการที่ +คุณนำงานไปใช้) อนุญาตแบบเดียวกัน — หากคุณดัดแปลง เปลี่ยนรูป หรื +อต่อเติมงานนี้ คุณต้องใช้สัญญาอนุญาตแบบเดียวกันหรือแบบที่เหมื +อนกับสัญญาอนุญาตที่ใช้กับงานนี้เท่านั้น` + +const threeByteUtf8 = txt_th + +// Taken from http://creativecommons.org/licenses/by-sa/2.0/jp/ +const txt_jp = `あなたの従うべき条件は以下の通りです。 +表示 — あなたは原著作者のクレジットを表示しなければなりません。 +継承 — もしあなたがこの作品を改変、変形または加工した場合、 +あなたはその結果生じた作品をこの作品と同一の許諾条件の下でのみ +頒布することができます。` + +// http://creativecommons.org/licenses/by-sa/2.5/cn/ +const txt_cn = `您可以自由: 复制、发行、展览、表演、放映、 +广播或通过信息网络传播本作品 创作演绎作品 +对本作品进行商业性使用 惟须遵守下列条件: +署名 — 您必须按照作者或者许可人指定的方式对作品进行署名。 +相同方式共享 — 如果您改变、转换本作品或者以本作品为基础进行创作, +您只能采用与本协议相同的许可协议发布基于本作品的演绎作品。` + +const txt_cjk = txt_cn + txt_jp + txt_kr +const txt_all = txt_vn + twoByteUtf8 + threeByteUtf8 + txt_cjk + +var txt_all_bytes = []byte(txt_all) diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/readwriter.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/readwriter.go new file mode 100644 index 000000000..4fa0e04b2 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/readwriter.go @@ -0,0 +1,126 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import "io" + +type normWriter struct { + rb reorderBuffer + w io.Writer + buf []byte +} + +// Write implements the standard write interface. If the last characters are +// not at a normalization boundary, the bytes will be buffered for the next +// write. The remaining bytes will be written on close. +func (w *normWriter) Write(data []byte) (n int, err error) { + // Process data in pieces to keep w.buf size bounded. + const chunk = 4000 + + for len(data) > 0 { + // Normalize into w.buf. + m := len(data) + if m > chunk { + m = chunk + } + w.rb.src = inputBytes(data[:m]) + w.rb.nsrc = m + w.buf = doAppend(&w.rb, w.buf, 0) + data = data[m:] + n += m + + // Write out complete prefix, save remainder. + // Note that lastBoundary looks back at most 31 runes. + i := lastBoundary(&w.rb.f, w.buf) + if i == -1 { + i = 0 + } + if i > 0 { + if _, err = w.w.Write(w.buf[:i]); err != nil { + break + } + bn := copy(w.buf, w.buf[i:]) + w.buf = w.buf[:bn] + } + } + return n, err +} + +// Close forces data that remains in the buffer to be written. +func (w *normWriter) Close() error { + if len(w.buf) > 0 { + _, err := w.w.Write(w.buf) + if err != nil { + return err + } + } + return nil +} + +// Writer returns a new writer that implements Write(b) +// by writing f(b) to w. The returned writer may use an +// an internal buffer to maintain state across Write calls. +// Calling its Close method writes any buffered data to w. +func (f Form) Writer(w io.Writer) io.WriteCloser { + wr := &normWriter{rb: reorderBuffer{}, w: w} + wr.rb.init(f, nil) + return wr +} + +type normReader struct { + rb reorderBuffer + r io.Reader + inbuf []byte + outbuf []byte + bufStart int + lastBoundary int + err error +} + +// Read implements the standard read interface. +func (r *normReader) Read(p []byte) (int, error) { + for { + if r.lastBoundary-r.bufStart > 0 { + n := copy(p, r.outbuf[r.bufStart:r.lastBoundary]) + r.bufStart += n + if r.lastBoundary-r.bufStart > 0 { + return n, nil + } + return n, r.err + } + if r.err != nil { + return 0, r.err + } + outn := copy(r.outbuf, r.outbuf[r.lastBoundary:]) + r.outbuf = r.outbuf[0:outn] + r.bufStart = 0 + + n, err := r.r.Read(r.inbuf) + r.rb.src = inputBytes(r.inbuf[0:n]) + r.rb.nsrc, r.err = n, err + if n > 0 { + r.outbuf = doAppend(&r.rb, r.outbuf, 0) + } + if err == io.EOF { + r.lastBoundary = len(r.outbuf) + } else { + r.lastBoundary = lastBoundary(&r.rb.f, r.outbuf) + if r.lastBoundary == -1 { + r.lastBoundary = 0 + } + } + } + panic("should not reach here") +} + +// Reader returns a new reader that implements Read +// by reading data from r and returning f(data). +func (f Form) Reader(r io.Reader) io.Reader { + const chunk = 4000 + buf := make([]byte, chunk) + rr := &normReader{rb: reorderBuffer{}, r: r, inbuf: buf} + rr.rb.init(f, buf) + return rr +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/readwriter_test.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/readwriter_test.go new file mode 100644 index 000000000..b7756ba24 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/readwriter_test.go @@ -0,0 +1,56 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "bytes" + "fmt" + "testing" +) + +var bufSizes = []int{1, 2, 3, 4, 5, 6, 7, 8, 100, 101, 102, 103, 4000, 4001, 4002, 4003} + +func readFunc(size int) appendFunc { + return func(f Form, out []byte, s string) []byte { + out = append(out, s...) + r := f.Reader(bytes.NewBuffer(out)) + buf := make([]byte, size) + result := []byte{} + for n, err := 0, error(nil); err == nil; { + n, err = r.Read(buf) + result = append(result, buf[:n]...) + } + return result + } +} + +func TestReader(t *testing.T) { + for _, s := range bufSizes { + name := fmt.Sprintf("TestReader%d", s) + runNormTests(t, name, readFunc(s)) + } +} + +func writeFunc(size int) appendFunc { + return func(f Form, out []byte, s string) []byte { + in := append(out, s...) + result := new(bytes.Buffer) + w := f.Writer(result) + buf := make([]byte, size) + for n := 0; len(in) > 0; in = in[n:] { + n = copy(buf, in) + _, _ = w.Write(buf[:n]) + } + w.Close() + return result.Bytes() + } +} + +func TestWriter(t *testing.T) { + for _, s := range bufSizes { + name := fmt.Sprintf("TestWriter%d", s) + runNormTests(t, name, writeFunc(s)) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/tables.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/tables.go new file mode 100644 index 000000000..22508818c --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/tables.go @@ -0,0 +1,7587 @@ +// This file was generated by go generate; DO NOT EDIT + +package norm + +const ( + // Version is the Unicode edition from which the tables are derived. + Version = "8.0.0" + + // MaxTransformChunkSize indicates the maximum number of bytes that Transform + // may need to write atomically for any Form. Making a destination buffer at + // least this size ensures that Transform can always make progress and that + // the user does not need to grow the buffer on an ErrShortDst. + MaxTransformChunkSize = 35 + maxNonStarters*4 +) + +var ccc = [55]uint8{ + 0, 1, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, + 84, 91, 103, 107, 118, 122, 129, 130, + 132, 202, 214, 216, 218, 220, 222, 224, + 226, 228, 230, 232, 233, 234, 240, +} + +const ( + firstMulti = 0x1869 + firstCCC = 0x2C9A + endMulti = 0x2F5C + firstLeadingCCC = 0x4A40 + firstCCCZeroExcept = 0x4A56 + firstStarterWithNLead = 0x4A7D + lastDecomp = 0x4A7F + maxDecomp = 0x8000 +) + +// decomps: 19071 bytes +var decomps = [...]byte{ + // Bytes 0 - 3f + 0x00, 0x41, 0x20, 0x41, 0x21, 0x41, 0x22, 0x41, + 0x23, 0x41, 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, + 0x27, 0x41, 0x28, 0x41, 0x29, 0x41, 0x2A, 0x41, + 0x2B, 0x41, 0x2C, 0x41, 0x2D, 0x41, 0x2E, 0x41, + 0x2F, 0x41, 0x30, 0x41, 0x31, 0x41, 0x32, 0x41, + 0x33, 0x41, 0x34, 0x41, 0x35, 0x41, 0x36, 0x41, + 0x37, 0x41, 0x38, 0x41, 0x39, 0x41, 0x3A, 0x41, + 0x3B, 0x41, 0x3C, 0x41, 0x3D, 0x41, 0x3E, 0x41, + // Bytes 40 - 7f + 0x3F, 0x41, 0x40, 0x41, 0x41, 0x41, 0x42, 0x41, + 0x43, 0x41, 0x44, 0x41, 0x45, 0x41, 0x46, 0x41, + 0x47, 0x41, 0x48, 0x41, 0x49, 0x41, 0x4A, 0x41, + 0x4B, 0x41, 0x4C, 0x41, 0x4D, 0x41, 0x4E, 0x41, + 0x4F, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, + 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, + 0x57, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x41, + 0x5B, 0x41, 0x5C, 0x41, 0x5D, 0x41, 0x5E, 0x41, + // Bytes 80 - bf + 0x5F, 0x41, 0x60, 0x41, 0x61, 0x41, 0x62, 0x41, + 0x63, 0x41, 0x64, 0x41, 0x65, 0x41, 0x66, 0x41, + 0x67, 0x41, 0x68, 0x41, 0x69, 0x41, 0x6A, 0x41, + 0x6B, 0x41, 0x6C, 0x41, 0x6D, 0x41, 0x6E, 0x41, + 0x6F, 0x41, 0x70, 0x41, 0x71, 0x41, 0x72, 0x41, + 0x73, 0x41, 0x74, 0x41, 0x75, 0x41, 0x76, 0x41, + 0x77, 0x41, 0x78, 0x41, 0x79, 0x41, 0x7A, 0x41, + 0x7B, 0x41, 0x7C, 0x41, 0x7D, 0x41, 0x7E, 0x42, + // Bytes c0 - ff + 0xC2, 0xA2, 0x42, 0xC2, 0xA3, 0x42, 0xC2, 0xA5, + 0x42, 0xC2, 0xA6, 0x42, 0xC2, 0xAC, 0x42, 0xC2, + 0xB7, 0x42, 0xC3, 0x86, 0x42, 0xC3, 0xB0, 0x42, + 0xC4, 0xA6, 0x42, 0xC4, 0xA7, 0x42, 0xC4, 0xB1, + 0x42, 0xC5, 0x8B, 0x42, 0xC5, 0x93, 0x42, 0xC6, + 0x8E, 0x42, 0xC6, 0x90, 0x42, 0xC6, 0xAB, 0x42, + 0xC8, 0xA2, 0x42, 0xC8, 0xB7, 0x42, 0xC9, 0x90, + 0x42, 0xC9, 0x91, 0x42, 0xC9, 0x92, 0x42, 0xC9, + // Bytes 100 - 13f + 0x94, 0x42, 0xC9, 0x95, 0x42, 0xC9, 0x99, 0x42, + 0xC9, 0x9B, 0x42, 0xC9, 0x9C, 0x42, 0xC9, 0x9F, + 0x42, 0xC9, 0xA1, 0x42, 0xC9, 0xA3, 0x42, 0xC9, + 0xA5, 0x42, 0xC9, 0xA6, 0x42, 0xC9, 0xA8, 0x42, + 0xC9, 0xA9, 0x42, 0xC9, 0xAA, 0x42, 0xC9, 0xAB, + 0x42, 0xC9, 0xAD, 0x42, 0xC9, 0xAF, 0x42, 0xC9, + 0xB0, 0x42, 0xC9, 0xB1, 0x42, 0xC9, 0xB2, 0x42, + 0xC9, 0xB3, 0x42, 0xC9, 0xB4, 0x42, 0xC9, 0xB5, + // Bytes 140 - 17f + 0x42, 0xC9, 0xB8, 0x42, 0xC9, 0xB9, 0x42, 0xC9, + 0xBB, 0x42, 0xCA, 0x81, 0x42, 0xCA, 0x82, 0x42, + 0xCA, 0x83, 0x42, 0xCA, 0x89, 0x42, 0xCA, 0x8A, + 0x42, 0xCA, 0x8B, 0x42, 0xCA, 0x8C, 0x42, 0xCA, + 0x90, 0x42, 0xCA, 0x91, 0x42, 0xCA, 0x92, 0x42, + 0xCA, 0x95, 0x42, 0xCA, 0x9D, 0x42, 0xCA, 0x9F, + 0x42, 0xCA, 0xB9, 0x42, 0xCE, 0x91, 0x42, 0xCE, + 0x92, 0x42, 0xCE, 0x93, 0x42, 0xCE, 0x94, 0x42, + // Bytes 180 - 1bf + 0xCE, 0x95, 0x42, 0xCE, 0x96, 0x42, 0xCE, 0x97, + 0x42, 0xCE, 0x98, 0x42, 0xCE, 0x99, 0x42, 0xCE, + 0x9A, 0x42, 0xCE, 0x9B, 0x42, 0xCE, 0x9C, 0x42, + 0xCE, 0x9D, 0x42, 0xCE, 0x9E, 0x42, 0xCE, 0x9F, + 0x42, 0xCE, 0xA0, 0x42, 0xCE, 0xA1, 0x42, 0xCE, + 0xA3, 0x42, 0xCE, 0xA4, 0x42, 0xCE, 0xA5, 0x42, + 0xCE, 0xA6, 0x42, 0xCE, 0xA7, 0x42, 0xCE, 0xA8, + 0x42, 0xCE, 0xA9, 0x42, 0xCE, 0xB1, 0x42, 0xCE, + // Bytes 1c0 - 1ff + 0xB2, 0x42, 0xCE, 0xB3, 0x42, 0xCE, 0xB4, 0x42, + 0xCE, 0xB5, 0x42, 0xCE, 0xB6, 0x42, 0xCE, 0xB7, + 0x42, 0xCE, 0xB8, 0x42, 0xCE, 0xB9, 0x42, 0xCE, + 0xBA, 0x42, 0xCE, 0xBB, 0x42, 0xCE, 0xBC, 0x42, + 0xCE, 0xBD, 0x42, 0xCE, 0xBE, 0x42, 0xCE, 0xBF, + 0x42, 0xCF, 0x80, 0x42, 0xCF, 0x81, 0x42, 0xCF, + 0x82, 0x42, 0xCF, 0x83, 0x42, 0xCF, 0x84, 0x42, + 0xCF, 0x85, 0x42, 0xCF, 0x86, 0x42, 0xCF, 0x87, + // Bytes 200 - 23f + 0x42, 0xCF, 0x88, 0x42, 0xCF, 0x89, 0x42, 0xCF, + 0x9C, 0x42, 0xCF, 0x9D, 0x42, 0xD0, 0xBD, 0x42, + 0xD1, 0x8A, 0x42, 0xD1, 0x8C, 0x42, 0xD7, 0x90, + 0x42, 0xD7, 0x91, 0x42, 0xD7, 0x92, 0x42, 0xD7, + 0x93, 0x42, 0xD7, 0x94, 0x42, 0xD7, 0x9B, 0x42, + 0xD7, 0x9C, 0x42, 0xD7, 0x9D, 0x42, 0xD7, 0xA2, + 0x42, 0xD7, 0xA8, 0x42, 0xD7, 0xAA, 0x42, 0xD8, + 0xA1, 0x42, 0xD8, 0xA7, 0x42, 0xD8, 0xA8, 0x42, + // Bytes 240 - 27f + 0xD8, 0xA9, 0x42, 0xD8, 0xAA, 0x42, 0xD8, 0xAB, + 0x42, 0xD8, 0xAC, 0x42, 0xD8, 0xAD, 0x42, 0xD8, + 0xAE, 0x42, 0xD8, 0xAF, 0x42, 0xD8, 0xB0, 0x42, + 0xD8, 0xB1, 0x42, 0xD8, 0xB2, 0x42, 0xD8, 0xB3, + 0x42, 0xD8, 0xB4, 0x42, 0xD8, 0xB5, 0x42, 0xD8, + 0xB6, 0x42, 0xD8, 0xB7, 0x42, 0xD8, 0xB8, 0x42, + 0xD8, 0xB9, 0x42, 0xD8, 0xBA, 0x42, 0xD9, 0x81, + 0x42, 0xD9, 0x82, 0x42, 0xD9, 0x83, 0x42, 0xD9, + // Bytes 280 - 2bf + 0x84, 0x42, 0xD9, 0x85, 0x42, 0xD9, 0x86, 0x42, + 0xD9, 0x87, 0x42, 0xD9, 0x88, 0x42, 0xD9, 0x89, + 0x42, 0xD9, 0x8A, 0x42, 0xD9, 0xAE, 0x42, 0xD9, + 0xAF, 0x42, 0xD9, 0xB1, 0x42, 0xD9, 0xB9, 0x42, + 0xD9, 0xBA, 0x42, 0xD9, 0xBB, 0x42, 0xD9, 0xBE, + 0x42, 0xD9, 0xBF, 0x42, 0xDA, 0x80, 0x42, 0xDA, + 0x83, 0x42, 0xDA, 0x84, 0x42, 0xDA, 0x86, 0x42, + 0xDA, 0x87, 0x42, 0xDA, 0x88, 0x42, 0xDA, 0x8C, + // Bytes 2c0 - 2ff + 0x42, 0xDA, 0x8D, 0x42, 0xDA, 0x8E, 0x42, 0xDA, + 0x91, 0x42, 0xDA, 0x98, 0x42, 0xDA, 0xA1, 0x42, + 0xDA, 0xA4, 0x42, 0xDA, 0xA6, 0x42, 0xDA, 0xA9, + 0x42, 0xDA, 0xAD, 0x42, 0xDA, 0xAF, 0x42, 0xDA, + 0xB1, 0x42, 0xDA, 0xB3, 0x42, 0xDA, 0xBA, 0x42, + 0xDA, 0xBB, 0x42, 0xDA, 0xBE, 0x42, 0xDB, 0x81, + 0x42, 0xDB, 0x85, 0x42, 0xDB, 0x86, 0x42, 0xDB, + 0x87, 0x42, 0xDB, 0x88, 0x42, 0xDB, 0x89, 0x42, + // Bytes 300 - 33f + 0xDB, 0x8B, 0x42, 0xDB, 0x8C, 0x42, 0xDB, 0x90, + 0x42, 0xDB, 0x92, 0x43, 0xE0, 0xBC, 0x8B, 0x43, + 0xE1, 0x83, 0x9C, 0x43, 0xE1, 0x84, 0x80, 0x43, + 0xE1, 0x84, 0x81, 0x43, 0xE1, 0x84, 0x82, 0x43, + 0xE1, 0x84, 0x83, 0x43, 0xE1, 0x84, 0x84, 0x43, + 0xE1, 0x84, 0x85, 0x43, 0xE1, 0x84, 0x86, 0x43, + 0xE1, 0x84, 0x87, 0x43, 0xE1, 0x84, 0x88, 0x43, + 0xE1, 0x84, 0x89, 0x43, 0xE1, 0x84, 0x8A, 0x43, + // Bytes 340 - 37f + 0xE1, 0x84, 0x8B, 0x43, 0xE1, 0x84, 0x8C, 0x43, + 0xE1, 0x84, 0x8D, 0x43, 0xE1, 0x84, 0x8E, 0x43, + 0xE1, 0x84, 0x8F, 0x43, 0xE1, 0x84, 0x90, 0x43, + 0xE1, 0x84, 0x91, 0x43, 0xE1, 0x84, 0x92, 0x43, + 0xE1, 0x84, 0x94, 0x43, 0xE1, 0x84, 0x95, 0x43, + 0xE1, 0x84, 0x9A, 0x43, 0xE1, 0x84, 0x9C, 0x43, + 0xE1, 0x84, 0x9D, 0x43, 0xE1, 0x84, 0x9E, 0x43, + 0xE1, 0x84, 0xA0, 0x43, 0xE1, 0x84, 0xA1, 0x43, + // Bytes 380 - 3bf + 0xE1, 0x84, 0xA2, 0x43, 0xE1, 0x84, 0xA3, 0x43, + 0xE1, 0x84, 0xA7, 0x43, 0xE1, 0x84, 0xA9, 0x43, + 0xE1, 0x84, 0xAB, 0x43, 0xE1, 0x84, 0xAC, 0x43, + 0xE1, 0x84, 0xAD, 0x43, 0xE1, 0x84, 0xAE, 0x43, + 0xE1, 0x84, 0xAF, 0x43, 0xE1, 0x84, 0xB2, 0x43, + 0xE1, 0x84, 0xB6, 0x43, 0xE1, 0x85, 0x80, 0x43, + 0xE1, 0x85, 0x87, 0x43, 0xE1, 0x85, 0x8C, 0x43, + 0xE1, 0x85, 0x97, 0x43, 0xE1, 0x85, 0x98, 0x43, + // Bytes 3c0 - 3ff + 0xE1, 0x85, 0x99, 0x43, 0xE1, 0x85, 0xA0, 0x43, + 0xE1, 0x86, 0x84, 0x43, 0xE1, 0x86, 0x85, 0x43, + 0xE1, 0x86, 0x88, 0x43, 0xE1, 0x86, 0x91, 0x43, + 0xE1, 0x86, 0x92, 0x43, 0xE1, 0x86, 0x94, 0x43, + 0xE1, 0x86, 0x9E, 0x43, 0xE1, 0x86, 0xA1, 0x43, + 0xE1, 0x87, 0x87, 0x43, 0xE1, 0x87, 0x88, 0x43, + 0xE1, 0x87, 0x8C, 0x43, 0xE1, 0x87, 0x8E, 0x43, + 0xE1, 0x87, 0x93, 0x43, 0xE1, 0x87, 0x97, 0x43, + // Bytes 400 - 43f + 0xE1, 0x87, 0x99, 0x43, 0xE1, 0x87, 0x9D, 0x43, + 0xE1, 0x87, 0x9F, 0x43, 0xE1, 0x87, 0xB1, 0x43, + 0xE1, 0x87, 0xB2, 0x43, 0xE1, 0xB4, 0x82, 0x43, + 0xE1, 0xB4, 0x96, 0x43, 0xE1, 0xB4, 0x97, 0x43, + 0xE1, 0xB4, 0x9C, 0x43, 0xE1, 0xB4, 0x9D, 0x43, + 0xE1, 0xB4, 0xA5, 0x43, 0xE1, 0xB5, 0xBB, 0x43, + 0xE1, 0xB6, 0x85, 0x43, 0xE2, 0x80, 0x82, 0x43, + 0xE2, 0x80, 0x83, 0x43, 0xE2, 0x80, 0x90, 0x43, + // Bytes 440 - 47f + 0xE2, 0x80, 0x93, 0x43, 0xE2, 0x80, 0x94, 0x43, + 0xE2, 0x82, 0xA9, 0x43, 0xE2, 0x86, 0x90, 0x43, + 0xE2, 0x86, 0x91, 0x43, 0xE2, 0x86, 0x92, 0x43, + 0xE2, 0x86, 0x93, 0x43, 0xE2, 0x88, 0x82, 0x43, + 0xE2, 0x88, 0x87, 0x43, 0xE2, 0x88, 0x91, 0x43, + 0xE2, 0x88, 0x92, 0x43, 0xE2, 0x94, 0x82, 0x43, + 0xE2, 0x96, 0xA0, 0x43, 0xE2, 0x97, 0x8B, 0x43, + 0xE2, 0xA6, 0x85, 0x43, 0xE2, 0xA6, 0x86, 0x43, + // Bytes 480 - 4bf + 0xE2, 0xB5, 0xA1, 0x43, 0xE3, 0x80, 0x81, 0x43, + 0xE3, 0x80, 0x82, 0x43, 0xE3, 0x80, 0x88, 0x43, + 0xE3, 0x80, 0x89, 0x43, 0xE3, 0x80, 0x8A, 0x43, + 0xE3, 0x80, 0x8B, 0x43, 0xE3, 0x80, 0x8C, 0x43, + 0xE3, 0x80, 0x8D, 0x43, 0xE3, 0x80, 0x8E, 0x43, + 0xE3, 0x80, 0x8F, 0x43, 0xE3, 0x80, 0x90, 0x43, + 0xE3, 0x80, 0x91, 0x43, 0xE3, 0x80, 0x92, 0x43, + 0xE3, 0x80, 0x94, 0x43, 0xE3, 0x80, 0x95, 0x43, + // Bytes 4c0 - 4ff + 0xE3, 0x80, 0x96, 0x43, 0xE3, 0x80, 0x97, 0x43, + 0xE3, 0x82, 0xA1, 0x43, 0xE3, 0x82, 0xA2, 0x43, + 0xE3, 0x82, 0xA3, 0x43, 0xE3, 0x82, 0xA4, 0x43, + 0xE3, 0x82, 0xA5, 0x43, 0xE3, 0x82, 0xA6, 0x43, + 0xE3, 0x82, 0xA7, 0x43, 0xE3, 0x82, 0xA8, 0x43, + 0xE3, 0x82, 0xA9, 0x43, 0xE3, 0x82, 0xAA, 0x43, + 0xE3, 0x82, 0xAB, 0x43, 0xE3, 0x82, 0xAD, 0x43, + 0xE3, 0x82, 0xAF, 0x43, 0xE3, 0x82, 0xB1, 0x43, + // Bytes 500 - 53f + 0xE3, 0x82, 0xB3, 0x43, 0xE3, 0x82, 0xB5, 0x43, + 0xE3, 0x82, 0xB7, 0x43, 0xE3, 0x82, 0xB9, 0x43, + 0xE3, 0x82, 0xBB, 0x43, 0xE3, 0x82, 0xBD, 0x43, + 0xE3, 0x82, 0xBF, 0x43, 0xE3, 0x83, 0x81, 0x43, + 0xE3, 0x83, 0x83, 0x43, 0xE3, 0x83, 0x84, 0x43, + 0xE3, 0x83, 0x86, 0x43, 0xE3, 0x83, 0x88, 0x43, + 0xE3, 0x83, 0x8A, 0x43, 0xE3, 0x83, 0x8B, 0x43, + 0xE3, 0x83, 0x8C, 0x43, 0xE3, 0x83, 0x8D, 0x43, + // Bytes 540 - 57f + 0xE3, 0x83, 0x8E, 0x43, 0xE3, 0x83, 0x8F, 0x43, + 0xE3, 0x83, 0x92, 0x43, 0xE3, 0x83, 0x95, 0x43, + 0xE3, 0x83, 0x98, 0x43, 0xE3, 0x83, 0x9B, 0x43, + 0xE3, 0x83, 0x9E, 0x43, 0xE3, 0x83, 0x9F, 0x43, + 0xE3, 0x83, 0xA0, 0x43, 0xE3, 0x83, 0xA1, 0x43, + 0xE3, 0x83, 0xA2, 0x43, 0xE3, 0x83, 0xA3, 0x43, + 0xE3, 0x83, 0xA4, 0x43, 0xE3, 0x83, 0xA5, 0x43, + 0xE3, 0x83, 0xA6, 0x43, 0xE3, 0x83, 0xA7, 0x43, + // Bytes 580 - 5bf + 0xE3, 0x83, 0xA8, 0x43, 0xE3, 0x83, 0xA9, 0x43, + 0xE3, 0x83, 0xAA, 0x43, 0xE3, 0x83, 0xAB, 0x43, + 0xE3, 0x83, 0xAC, 0x43, 0xE3, 0x83, 0xAD, 0x43, + 0xE3, 0x83, 0xAF, 0x43, 0xE3, 0x83, 0xB0, 0x43, + 0xE3, 0x83, 0xB1, 0x43, 0xE3, 0x83, 0xB2, 0x43, + 0xE3, 0x83, 0xB3, 0x43, 0xE3, 0x83, 0xBB, 0x43, + 0xE3, 0x83, 0xBC, 0x43, 0xE3, 0x92, 0x9E, 0x43, + 0xE3, 0x92, 0xB9, 0x43, 0xE3, 0x92, 0xBB, 0x43, + // Bytes 5c0 - 5ff + 0xE3, 0x93, 0x9F, 0x43, 0xE3, 0x94, 0x95, 0x43, + 0xE3, 0x9B, 0xAE, 0x43, 0xE3, 0x9B, 0xBC, 0x43, + 0xE3, 0x9E, 0x81, 0x43, 0xE3, 0xA0, 0xAF, 0x43, + 0xE3, 0xA1, 0xA2, 0x43, 0xE3, 0xA1, 0xBC, 0x43, + 0xE3, 0xA3, 0x87, 0x43, 0xE3, 0xA3, 0xA3, 0x43, + 0xE3, 0xA4, 0x9C, 0x43, 0xE3, 0xA4, 0xBA, 0x43, + 0xE3, 0xA8, 0xAE, 0x43, 0xE3, 0xA9, 0xAC, 0x43, + 0xE3, 0xAB, 0xA4, 0x43, 0xE3, 0xAC, 0x88, 0x43, + // Bytes 600 - 63f + 0xE3, 0xAC, 0x99, 0x43, 0xE3, 0xAD, 0x89, 0x43, + 0xE3, 0xAE, 0x9D, 0x43, 0xE3, 0xB0, 0x98, 0x43, + 0xE3, 0xB1, 0x8E, 0x43, 0xE3, 0xB4, 0xB3, 0x43, + 0xE3, 0xB6, 0x96, 0x43, 0xE3, 0xBA, 0xAC, 0x43, + 0xE3, 0xBA, 0xB8, 0x43, 0xE3, 0xBC, 0x9B, 0x43, + 0xE3, 0xBF, 0xBC, 0x43, 0xE4, 0x80, 0x88, 0x43, + 0xE4, 0x80, 0x98, 0x43, 0xE4, 0x80, 0xB9, 0x43, + 0xE4, 0x81, 0x86, 0x43, 0xE4, 0x82, 0x96, 0x43, + // Bytes 640 - 67f + 0xE4, 0x83, 0xA3, 0x43, 0xE4, 0x84, 0xAF, 0x43, + 0xE4, 0x88, 0x82, 0x43, 0xE4, 0x88, 0xA7, 0x43, + 0xE4, 0x8A, 0xA0, 0x43, 0xE4, 0x8C, 0x81, 0x43, + 0xE4, 0x8C, 0xB4, 0x43, 0xE4, 0x8D, 0x99, 0x43, + 0xE4, 0x8F, 0x95, 0x43, 0xE4, 0x8F, 0x99, 0x43, + 0xE4, 0x90, 0x8B, 0x43, 0xE4, 0x91, 0xAB, 0x43, + 0xE4, 0x94, 0xAB, 0x43, 0xE4, 0x95, 0x9D, 0x43, + 0xE4, 0x95, 0xA1, 0x43, 0xE4, 0x95, 0xAB, 0x43, + // Bytes 680 - 6bf + 0xE4, 0x97, 0x97, 0x43, 0xE4, 0x97, 0xB9, 0x43, + 0xE4, 0x98, 0xB5, 0x43, 0xE4, 0x9A, 0xBE, 0x43, + 0xE4, 0x9B, 0x87, 0x43, 0xE4, 0xA6, 0x95, 0x43, + 0xE4, 0xA7, 0xA6, 0x43, 0xE4, 0xA9, 0xAE, 0x43, + 0xE4, 0xA9, 0xB6, 0x43, 0xE4, 0xAA, 0xB2, 0x43, + 0xE4, 0xAC, 0xB3, 0x43, 0xE4, 0xAF, 0x8E, 0x43, + 0xE4, 0xB3, 0x8E, 0x43, 0xE4, 0xB3, 0xAD, 0x43, + 0xE4, 0xB3, 0xB8, 0x43, 0xE4, 0xB5, 0x96, 0x43, + // Bytes 6c0 - 6ff + 0xE4, 0xB8, 0x80, 0x43, 0xE4, 0xB8, 0x81, 0x43, + 0xE4, 0xB8, 0x83, 0x43, 0xE4, 0xB8, 0x89, 0x43, + 0xE4, 0xB8, 0x8A, 0x43, 0xE4, 0xB8, 0x8B, 0x43, + 0xE4, 0xB8, 0x8D, 0x43, 0xE4, 0xB8, 0x99, 0x43, + 0xE4, 0xB8, 0xA6, 0x43, 0xE4, 0xB8, 0xA8, 0x43, + 0xE4, 0xB8, 0xAD, 0x43, 0xE4, 0xB8, 0xB2, 0x43, + 0xE4, 0xB8, 0xB6, 0x43, 0xE4, 0xB8, 0xB8, 0x43, + 0xE4, 0xB8, 0xB9, 0x43, 0xE4, 0xB8, 0xBD, 0x43, + // Bytes 700 - 73f + 0xE4, 0xB8, 0xBF, 0x43, 0xE4, 0xB9, 0x81, 0x43, + 0xE4, 0xB9, 0x99, 0x43, 0xE4, 0xB9, 0x9D, 0x43, + 0xE4, 0xBA, 0x82, 0x43, 0xE4, 0xBA, 0x85, 0x43, + 0xE4, 0xBA, 0x86, 0x43, 0xE4, 0xBA, 0x8C, 0x43, + 0xE4, 0xBA, 0x94, 0x43, 0xE4, 0xBA, 0xA0, 0x43, + 0xE4, 0xBA, 0xA4, 0x43, 0xE4, 0xBA, 0xAE, 0x43, + 0xE4, 0xBA, 0xBA, 0x43, 0xE4, 0xBB, 0x80, 0x43, + 0xE4, 0xBB, 0x8C, 0x43, 0xE4, 0xBB, 0xA4, 0x43, + // Bytes 740 - 77f + 0xE4, 0xBC, 0x81, 0x43, 0xE4, 0xBC, 0x91, 0x43, + 0xE4, 0xBD, 0xA0, 0x43, 0xE4, 0xBE, 0x80, 0x43, + 0xE4, 0xBE, 0x86, 0x43, 0xE4, 0xBE, 0x8B, 0x43, + 0xE4, 0xBE, 0xAE, 0x43, 0xE4, 0xBE, 0xBB, 0x43, + 0xE4, 0xBE, 0xBF, 0x43, 0xE5, 0x80, 0x82, 0x43, + 0xE5, 0x80, 0xAB, 0x43, 0xE5, 0x81, 0xBA, 0x43, + 0xE5, 0x82, 0x99, 0x43, 0xE5, 0x83, 0x8F, 0x43, + 0xE5, 0x83, 0x9A, 0x43, 0xE5, 0x83, 0xA7, 0x43, + // Bytes 780 - 7bf + 0xE5, 0x84, 0xAA, 0x43, 0xE5, 0x84, 0xBF, 0x43, + 0xE5, 0x85, 0x80, 0x43, 0xE5, 0x85, 0x85, 0x43, + 0xE5, 0x85, 0x8D, 0x43, 0xE5, 0x85, 0x94, 0x43, + 0xE5, 0x85, 0xA4, 0x43, 0xE5, 0x85, 0xA5, 0x43, + 0xE5, 0x85, 0xA7, 0x43, 0xE5, 0x85, 0xA8, 0x43, + 0xE5, 0x85, 0xA9, 0x43, 0xE5, 0x85, 0xAB, 0x43, + 0xE5, 0x85, 0xAD, 0x43, 0xE5, 0x85, 0xB7, 0x43, + 0xE5, 0x86, 0x80, 0x43, 0xE5, 0x86, 0x82, 0x43, + // Bytes 7c0 - 7ff + 0xE5, 0x86, 0x8D, 0x43, 0xE5, 0x86, 0x92, 0x43, + 0xE5, 0x86, 0x95, 0x43, 0xE5, 0x86, 0x96, 0x43, + 0xE5, 0x86, 0x97, 0x43, 0xE5, 0x86, 0x99, 0x43, + 0xE5, 0x86, 0xA4, 0x43, 0xE5, 0x86, 0xAB, 0x43, + 0xE5, 0x86, 0xAC, 0x43, 0xE5, 0x86, 0xB5, 0x43, + 0xE5, 0x86, 0xB7, 0x43, 0xE5, 0x87, 0x89, 0x43, + 0xE5, 0x87, 0x8C, 0x43, 0xE5, 0x87, 0x9C, 0x43, + 0xE5, 0x87, 0x9E, 0x43, 0xE5, 0x87, 0xA0, 0x43, + // Bytes 800 - 83f + 0xE5, 0x87, 0xB5, 0x43, 0xE5, 0x88, 0x80, 0x43, + 0xE5, 0x88, 0x83, 0x43, 0xE5, 0x88, 0x87, 0x43, + 0xE5, 0x88, 0x97, 0x43, 0xE5, 0x88, 0x9D, 0x43, + 0xE5, 0x88, 0xA9, 0x43, 0xE5, 0x88, 0xBA, 0x43, + 0xE5, 0x88, 0xBB, 0x43, 0xE5, 0x89, 0x86, 0x43, + 0xE5, 0x89, 0x8D, 0x43, 0xE5, 0x89, 0xB2, 0x43, + 0xE5, 0x89, 0xB7, 0x43, 0xE5, 0x8A, 0x89, 0x43, + 0xE5, 0x8A, 0x9B, 0x43, 0xE5, 0x8A, 0xA3, 0x43, + // Bytes 840 - 87f + 0xE5, 0x8A, 0xB3, 0x43, 0xE5, 0x8A, 0xB4, 0x43, + 0xE5, 0x8B, 0x87, 0x43, 0xE5, 0x8B, 0x89, 0x43, + 0xE5, 0x8B, 0x92, 0x43, 0xE5, 0x8B, 0x9E, 0x43, + 0xE5, 0x8B, 0xA4, 0x43, 0xE5, 0x8B, 0xB5, 0x43, + 0xE5, 0x8B, 0xB9, 0x43, 0xE5, 0x8B, 0xBA, 0x43, + 0xE5, 0x8C, 0x85, 0x43, 0xE5, 0x8C, 0x86, 0x43, + 0xE5, 0x8C, 0x95, 0x43, 0xE5, 0x8C, 0x97, 0x43, + 0xE5, 0x8C, 0x9A, 0x43, 0xE5, 0x8C, 0xB8, 0x43, + // Bytes 880 - 8bf + 0xE5, 0x8C, 0xBB, 0x43, 0xE5, 0x8C, 0xBF, 0x43, + 0xE5, 0x8D, 0x81, 0x43, 0xE5, 0x8D, 0x84, 0x43, + 0xE5, 0x8D, 0x85, 0x43, 0xE5, 0x8D, 0x89, 0x43, + 0xE5, 0x8D, 0x91, 0x43, 0xE5, 0x8D, 0x94, 0x43, + 0xE5, 0x8D, 0x9A, 0x43, 0xE5, 0x8D, 0x9C, 0x43, + 0xE5, 0x8D, 0xA9, 0x43, 0xE5, 0x8D, 0xB0, 0x43, + 0xE5, 0x8D, 0xB3, 0x43, 0xE5, 0x8D, 0xB5, 0x43, + 0xE5, 0x8D, 0xBD, 0x43, 0xE5, 0x8D, 0xBF, 0x43, + // Bytes 8c0 - 8ff + 0xE5, 0x8E, 0x82, 0x43, 0xE5, 0x8E, 0xB6, 0x43, + 0xE5, 0x8F, 0x83, 0x43, 0xE5, 0x8F, 0x88, 0x43, + 0xE5, 0x8F, 0x8A, 0x43, 0xE5, 0x8F, 0x8C, 0x43, + 0xE5, 0x8F, 0x9F, 0x43, 0xE5, 0x8F, 0xA3, 0x43, + 0xE5, 0x8F, 0xA5, 0x43, 0xE5, 0x8F, 0xAB, 0x43, + 0xE5, 0x8F, 0xAF, 0x43, 0xE5, 0x8F, 0xB1, 0x43, + 0xE5, 0x8F, 0xB3, 0x43, 0xE5, 0x90, 0x86, 0x43, + 0xE5, 0x90, 0x88, 0x43, 0xE5, 0x90, 0x8D, 0x43, + // Bytes 900 - 93f + 0xE5, 0x90, 0x8F, 0x43, 0xE5, 0x90, 0x9D, 0x43, + 0xE5, 0x90, 0xB8, 0x43, 0xE5, 0x90, 0xB9, 0x43, + 0xE5, 0x91, 0x82, 0x43, 0xE5, 0x91, 0x88, 0x43, + 0xE5, 0x91, 0xA8, 0x43, 0xE5, 0x92, 0x9E, 0x43, + 0xE5, 0x92, 0xA2, 0x43, 0xE5, 0x92, 0xBD, 0x43, + 0xE5, 0x93, 0xB6, 0x43, 0xE5, 0x94, 0x90, 0x43, + 0xE5, 0x95, 0x8F, 0x43, 0xE5, 0x95, 0x93, 0x43, + 0xE5, 0x95, 0x95, 0x43, 0xE5, 0x95, 0xA3, 0x43, + // Bytes 940 - 97f + 0xE5, 0x96, 0x84, 0x43, 0xE5, 0x96, 0x87, 0x43, + 0xE5, 0x96, 0x99, 0x43, 0xE5, 0x96, 0x9D, 0x43, + 0xE5, 0x96, 0xAB, 0x43, 0xE5, 0x96, 0xB3, 0x43, + 0xE5, 0x96, 0xB6, 0x43, 0xE5, 0x97, 0x80, 0x43, + 0xE5, 0x97, 0x82, 0x43, 0xE5, 0x97, 0xA2, 0x43, + 0xE5, 0x98, 0x86, 0x43, 0xE5, 0x99, 0x91, 0x43, + 0xE5, 0x99, 0xA8, 0x43, 0xE5, 0x99, 0xB4, 0x43, + 0xE5, 0x9B, 0x97, 0x43, 0xE5, 0x9B, 0x9B, 0x43, + // Bytes 980 - 9bf + 0xE5, 0x9B, 0xB9, 0x43, 0xE5, 0x9C, 0x96, 0x43, + 0xE5, 0x9C, 0x97, 0x43, 0xE5, 0x9C, 0x9F, 0x43, + 0xE5, 0x9C, 0xB0, 0x43, 0xE5, 0x9E, 0x8B, 0x43, + 0xE5, 0x9F, 0x8E, 0x43, 0xE5, 0x9F, 0xB4, 0x43, + 0xE5, 0xA0, 0x8D, 0x43, 0xE5, 0xA0, 0xB1, 0x43, + 0xE5, 0xA0, 0xB2, 0x43, 0xE5, 0xA1, 0x80, 0x43, + 0xE5, 0xA1, 0x9A, 0x43, 0xE5, 0xA1, 0x9E, 0x43, + 0xE5, 0xA2, 0xA8, 0x43, 0xE5, 0xA2, 0xAC, 0x43, + // Bytes 9c0 - 9ff + 0xE5, 0xA2, 0xB3, 0x43, 0xE5, 0xA3, 0x98, 0x43, + 0xE5, 0xA3, 0x9F, 0x43, 0xE5, 0xA3, 0xAB, 0x43, + 0xE5, 0xA3, 0xAE, 0x43, 0xE5, 0xA3, 0xB0, 0x43, + 0xE5, 0xA3, 0xB2, 0x43, 0xE5, 0xA3, 0xB7, 0x43, + 0xE5, 0xA4, 0x82, 0x43, 0xE5, 0xA4, 0x86, 0x43, + 0xE5, 0xA4, 0x8A, 0x43, 0xE5, 0xA4, 0x95, 0x43, + 0xE5, 0xA4, 0x9A, 0x43, 0xE5, 0xA4, 0x9C, 0x43, + 0xE5, 0xA4, 0xA2, 0x43, 0xE5, 0xA4, 0xA7, 0x43, + // Bytes a00 - a3f + 0xE5, 0xA4, 0xA9, 0x43, 0xE5, 0xA5, 0x84, 0x43, + 0xE5, 0xA5, 0x88, 0x43, 0xE5, 0xA5, 0x91, 0x43, + 0xE5, 0xA5, 0x94, 0x43, 0xE5, 0xA5, 0xA2, 0x43, + 0xE5, 0xA5, 0xB3, 0x43, 0xE5, 0xA7, 0x98, 0x43, + 0xE5, 0xA7, 0xAC, 0x43, 0xE5, 0xA8, 0x9B, 0x43, + 0xE5, 0xA8, 0xA7, 0x43, 0xE5, 0xA9, 0xA2, 0x43, + 0xE5, 0xA9, 0xA6, 0x43, 0xE5, 0xAA, 0xB5, 0x43, + 0xE5, 0xAC, 0x88, 0x43, 0xE5, 0xAC, 0xA8, 0x43, + // Bytes a40 - a7f + 0xE5, 0xAC, 0xBE, 0x43, 0xE5, 0xAD, 0x90, 0x43, + 0xE5, 0xAD, 0x97, 0x43, 0xE5, 0xAD, 0xA6, 0x43, + 0xE5, 0xAE, 0x80, 0x43, 0xE5, 0xAE, 0x85, 0x43, + 0xE5, 0xAE, 0x97, 0x43, 0xE5, 0xAF, 0x83, 0x43, + 0xE5, 0xAF, 0x98, 0x43, 0xE5, 0xAF, 0xA7, 0x43, + 0xE5, 0xAF, 0xAE, 0x43, 0xE5, 0xAF, 0xB3, 0x43, + 0xE5, 0xAF, 0xB8, 0x43, 0xE5, 0xAF, 0xBF, 0x43, + 0xE5, 0xB0, 0x86, 0x43, 0xE5, 0xB0, 0x8F, 0x43, + // Bytes a80 - abf + 0xE5, 0xB0, 0xA2, 0x43, 0xE5, 0xB0, 0xB8, 0x43, + 0xE5, 0xB0, 0xBF, 0x43, 0xE5, 0xB1, 0xA0, 0x43, + 0xE5, 0xB1, 0xA2, 0x43, 0xE5, 0xB1, 0xA4, 0x43, + 0xE5, 0xB1, 0xA5, 0x43, 0xE5, 0xB1, 0xAE, 0x43, + 0xE5, 0xB1, 0xB1, 0x43, 0xE5, 0xB2, 0x8D, 0x43, + 0xE5, 0xB3, 0x80, 0x43, 0xE5, 0xB4, 0x99, 0x43, + 0xE5, 0xB5, 0x83, 0x43, 0xE5, 0xB5, 0x90, 0x43, + 0xE5, 0xB5, 0xAB, 0x43, 0xE5, 0xB5, 0xAE, 0x43, + // Bytes ac0 - aff + 0xE5, 0xB5, 0xBC, 0x43, 0xE5, 0xB6, 0xB2, 0x43, + 0xE5, 0xB6, 0xBA, 0x43, 0xE5, 0xB7, 0x9B, 0x43, + 0xE5, 0xB7, 0xA1, 0x43, 0xE5, 0xB7, 0xA2, 0x43, + 0xE5, 0xB7, 0xA5, 0x43, 0xE5, 0xB7, 0xA6, 0x43, + 0xE5, 0xB7, 0xB1, 0x43, 0xE5, 0xB7, 0xBD, 0x43, + 0xE5, 0xB7, 0xBE, 0x43, 0xE5, 0xB8, 0xA8, 0x43, + 0xE5, 0xB8, 0xBD, 0x43, 0xE5, 0xB9, 0xA9, 0x43, + 0xE5, 0xB9, 0xB2, 0x43, 0xE5, 0xB9, 0xB4, 0x43, + // Bytes b00 - b3f + 0xE5, 0xB9, 0xBA, 0x43, 0xE5, 0xB9, 0xBC, 0x43, + 0xE5, 0xB9, 0xBF, 0x43, 0xE5, 0xBA, 0xA6, 0x43, + 0xE5, 0xBA, 0xB0, 0x43, 0xE5, 0xBA, 0xB3, 0x43, + 0xE5, 0xBA, 0xB6, 0x43, 0xE5, 0xBB, 0x89, 0x43, + 0xE5, 0xBB, 0x8A, 0x43, 0xE5, 0xBB, 0x92, 0x43, + 0xE5, 0xBB, 0x93, 0x43, 0xE5, 0xBB, 0x99, 0x43, + 0xE5, 0xBB, 0xAC, 0x43, 0xE5, 0xBB, 0xB4, 0x43, + 0xE5, 0xBB, 0xBE, 0x43, 0xE5, 0xBC, 0x84, 0x43, + // Bytes b40 - b7f + 0xE5, 0xBC, 0x8B, 0x43, 0xE5, 0xBC, 0x93, 0x43, + 0xE5, 0xBC, 0xA2, 0x43, 0xE5, 0xBD, 0x90, 0x43, + 0xE5, 0xBD, 0x93, 0x43, 0xE5, 0xBD, 0xA1, 0x43, + 0xE5, 0xBD, 0xA2, 0x43, 0xE5, 0xBD, 0xA9, 0x43, + 0xE5, 0xBD, 0xAB, 0x43, 0xE5, 0xBD, 0xB3, 0x43, + 0xE5, 0xBE, 0x8B, 0x43, 0xE5, 0xBE, 0x8C, 0x43, + 0xE5, 0xBE, 0x97, 0x43, 0xE5, 0xBE, 0x9A, 0x43, + 0xE5, 0xBE, 0xA9, 0x43, 0xE5, 0xBE, 0xAD, 0x43, + // Bytes b80 - bbf + 0xE5, 0xBF, 0x83, 0x43, 0xE5, 0xBF, 0x8D, 0x43, + 0xE5, 0xBF, 0x97, 0x43, 0xE5, 0xBF, 0xB5, 0x43, + 0xE5, 0xBF, 0xB9, 0x43, 0xE6, 0x80, 0x92, 0x43, + 0xE6, 0x80, 0x9C, 0x43, 0xE6, 0x81, 0xB5, 0x43, + 0xE6, 0x82, 0x81, 0x43, 0xE6, 0x82, 0x94, 0x43, + 0xE6, 0x83, 0x87, 0x43, 0xE6, 0x83, 0x98, 0x43, + 0xE6, 0x83, 0xA1, 0x43, 0xE6, 0x84, 0x88, 0x43, + 0xE6, 0x85, 0x84, 0x43, 0xE6, 0x85, 0x88, 0x43, + // Bytes bc0 - bff + 0xE6, 0x85, 0x8C, 0x43, 0xE6, 0x85, 0x8E, 0x43, + 0xE6, 0x85, 0xA0, 0x43, 0xE6, 0x85, 0xA8, 0x43, + 0xE6, 0x85, 0xBA, 0x43, 0xE6, 0x86, 0x8E, 0x43, + 0xE6, 0x86, 0x90, 0x43, 0xE6, 0x86, 0xA4, 0x43, + 0xE6, 0x86, 0xAF, 0x43, 0xE6, 0x86, 0xB2, 0x43, + 0xE6, 0x87, 0x9E, 0x43, 0xE6, 0x87, 0xB2, 0x43, + 0xE6, 0x87, 0xB6, 0x43, 0xE6, 0x88, 0x80, 0x43, + 0xE6, 0x88, 0x88, 0x43, 0xE6, 0x88, 0x90, 0x43, + // Bytes c00 - c3f + 0xE6, 0x88, 0x9B, 0x43, 0xE6, 0x88, 0xAE, 0x43, + 0xE6, 0x88, 0xB4, 0x43, 0xE6, 0x88, 0xB6, 0x43, + 0xE6, 0x89, 0x8B, 0x43, 0xE6, 0x89, 0x93, 0x43, + 0xE6, 0x89, 0x9D, 0x43, 0xE6, 0x8A, 0x95, 0x43, + 0xE6, 0x8A, 0xB1, 0x43, 0xE6, 0x8B, 0x89, 0x43, + 0xE6, 0x8B, 0x8F, 0x43, 0xE6, 0x8B, 0x93, 0x43, + 0xE6, 0x8B, 0x94, 0x43, 0xE6, 0x8B, 0xBC, 0x43, + 0xE6, 0x8B, 0xBE, 0x43, 0xE6, 0x8C, 0x87, 0x43, + // Bytes c40 - c7f + 0xE6, 0x8C, 0xBD, 0x43, 0xE6, 0x8D, 0x90, 0x43, + 0xE6, 0x8D, 0x95, 0x43, 0xE6, 0x8D, 0xA8, 0x43, + 0xE6, 0x8D, 0xBB, 0x43, 0xE6, 0x8E, 0x83, 0x43, + 0xE6, 0x8E, 0xA0, 0x43, 0xE6, 0x8E, 0xA9, 0x43, + 0xE6, 0x8F, 0x84, 0x43, 0xE6, 0x8F, 0x85, 0x43, + 0xE6, 0x8F, 0xA4, 0x43, 0xE6, 0x90, 0x9C, 0x43, + 0xE6, 0x90, 0xA2, 0x43, 0xE6, 0x91, 0x92, 0x43, + 0xE6, 0x91, 0xA9, 0x43, 0xE6, 0x91, 0xB7, 0x43, + // Bytes c80 - cbf + 0xE6, 0x91, 0xBE, 0x43, 0xE6, 0x92, 0x9A, 0x43, + 0xE6, 0x92, 0x9D, 0x43, 0xE6, 0x93, 0x84, 0x43, + 0xE6, 0x94, 0xAF, 0x43, 0xE6, 0x94, 0xB4, 0x43, + 0xE6, 0x95, 0x8F, 0x43, 0xE6, 0x95, 0x96, 0x43, + 0xE6, 0x95, 0xAC, 0x43, 0xE6, 0x95, 0xB8, 0x43, + 0xE6, 0x96, 0x87, 0x43, 0xE6, 0x96, 0x97, 0x43, + 0xE6, 0x96, 0x99, 0x43, 0xE6, 0x96, 0xA4, 0x43, + 0xE6, 0x96, 0xB0, 0x43, 0xE6, 0x96, 0xB9, 0x43, + // Bytes cc0 - cff + 0xE6, 0x97, 0x85, 0x43, 0xE6, 0x97, 0xA0, 0x43, + 0xE6, 0x97, 0xA2, 0x43, 0xE6, 0x97, 0xA3, 0x43, + 0xE6, 0x97, 0xA5, 0x43, 0xE6, 0x98, 0x93, 0x43, + 0xE6, 0x98, 0xA0, 0x43, 0xE6, 0x99, 0x89, 0x43, + 0xE6, 0x99, 0xB4, 0x43, 0xE6, 0x9A, 0x88, 0x43, + 0xE6, 0x9A, 0x91, 0x43, 0xE6, 0x9A, 0x9C, 0x43, + 0xE6, 0x9A, 0xB4, 0x43, 0xE6, 0x9B, 0x86, 0x43, + 0xE6, 0x9B, 0xB0, 0x43, 0xE6, 0x9B, 0xB4, 0x43, + // Bytes d00 - d3f + 0xE6, 0x9B, 0xB8, 0x43, 0xE6, 0x9C, 0x80, 0x43, + 0xE6, 0x9C, 0x88, 0x43, 0xE6, 0x9C, 0x89, 0x43, + 0xE6, 0x9C, 0x97, 0x43, 0xE6, 0x9C, 0x9B, 0x43, + 0xE6, 0x9C, 0xA1, 0x43, 0xE6, 0x9C, 0xA8, 0x43, + 0xE6, 0x9D, 0x8E, 0x43, 0xE6, 0x9D, 0x93, 0x43, + 0xE6, 0x9D, 0x96, 0x43, 0xE6, 0x9D, 0x9E, 0x43, + 0xE6, 0x9D, 0xBB, 0x43, 0xE6, 0x9E, 0x85, 0x43, + 0xE6, 0x9E, 0x97, 0x43, 0xE6, 0x9F, 0xB3, 0x43, + // Bytes d40 - d7f + 0xE6, 0x9F, 0xBA, 0x43, 0xE6, 0xA0, 0x97, 0x43, + 0xE6, 0xA0, 0x9F, 0x43, 0xE6, 0xA0, 0xAA, 0x43, + 0xE6, 0xA1, 0x92, 0x43, 0xE6, 0xA2, 0x81, 0x43, + 0xE6, 0xA2, 0x85, 0x43, 0xE6, 0xA2, 0x8E, 0x43, + 0xE6, 0xA2, 0xA8, 0x43, 0xE6, 0xA4, 0x94, 0x43, + 0xE6, 0xA5, 0x82, 0x43, 0xE6, 0xA6, 0xA3, 0x43, + 0xE6, 0xA7, 0xAA, 0x43, 0xE6, 0xA8, 0x82, 0x43, + 0xE6, 0xA8, 0x93, 0x43, 0xE6, 0xAA, 0xA8, 0x43, + // Bytes d80 - dbf + 0xE6, 0xAB, 0x93, 0x43, 0xE6, 0xAB, 0x9B, 0x43, + 0xE6, 0xAC, 0x84, 0x43, 0xE6, 0xAC, 0xA0, 0x43, + 0xE6, 0xAC, 0xA1, 0x43, 0xE6, 0xAD, 0x94, 0x43, + 0xE6, 0xAD, 0xA2, 0x43, 0xE6, 0xAD, 0xA3, 0x43, + 0xE6, 0xAD, 0xB2, 0x43, 0xE6, 0xAD, 0xB7, 0x43, + 0xE6, 0xAD, 0xB9, 0x43, 0xE6, 0xAE, 0x9F, 0x43, + 0xE6, 0xAE, 0xAE, 0x43, 0xE6, 0xAE, 0xB3, 0x43, + 0xE6, 0xAE, 0xBA, 0x43, 0xE6, 0xAE, 0xBB, 0x43, + // Bytes dc0 - dff + 0xE6, 0xAF, 0x8B, 0x43, 0xE6, 0xAF, 0x8D, 0x43, + 0xE6, 0xAF, 0x94, 0x43, 0xE6, 0xAF, 0x9B, 0x43, + 0xE6, 0xB0, 0x8F, 0x43, 0xE6, 0xB0, 0x94, 0x43, + 0xE6, 0xB0, 0xB4, 0x43, 0xE6, 0xB1, 0x8E, 0x43, + 0xE6, 0xB1, 0xA7, 0x43, 0xE6, 0xB2, 0x88, 0x43, + 0xE6, 0xB2, 0xBF, 0x43, 0xE6, 0xB3, 0x8C, 0x43, + 0xE6, 0xB3, 0x8D, 0x43, 0xE6, 0xB3, 0xA5, 0x43, + 0xE6, 0xB3, 0xA8, 0x43, 0xE6, 0xB4, 0x96, 0x43, + // Bytes e00 - e3f + 0xE6, 0xB4, 0x9B, 0x43, 0xE6, 0xB4, 0x9E, 0x43, + 0xE6, 0xB4, 0xB4, 0x43, 0xE6, 0xB4, 0xBE, 0x43, + 0xE6, 0xB5, 0x81, 0x43, 0xE6, 0xB5, 0xA9, 0x43, + 0xE6, 0xB5, 0xAA, 0x43, 0xE6, 0xB5, 0xB7, 0x43, + 0xE6, 0xB5, 0xB8, 0x43, 0xE6, 0xB6, 0x85, 0x43, + 0xE6, 0xB7, 0x8B, 0x43, 0xE6, 0xB7, 0x9A, 0x43, + 0xE6, 0xB7, 0xAA, 0x43, 0xE6, 0xB7, 0xB9, 0x43, + 0xE6, 0xB8, 0x9A, 0x43, 0xE6, 0xB8, 0xAF, 0x43, + // Bytes e40 - e7f + 0xE6, 0xB9, 0xAE, 0x43, 0xE6, 0xBA, 0x80, 0x43, + 0xE6, 0xBA, 0x9C, 0x43, 0xE6, 0xBA, 0xBA, 0x43, + 0xE6, 0xBB, 0x87, 0x43, 0xE6, 0xBB, 0x8B, 0x43, + 0xE6, 0xBB, 0x91, 0x43, 0xE6, 0xBB, 0x9B, 0x43, + 0xE6, 0xBC, 0x8F, 0x43, 0xE6, 0xBC, 0x94, 0x43, + 0xE6, 0xBC, 0xA2, 0x43, 0xE6, 0xBC, 0xA3, 0x43, + 0xE6, 0xBD, 0xAE, 0x43, 0xE6, 0xBF, 0x86, 0x43, + 0xE6, 0xBF, 0xAB, 0x43, 0xE6, 0xBF, 0xBE, 0x43, + // Bytes e80 - ebf + 0xE7, 0x80, 0x9B, 0x43, 0xE7, 0x80, 0x9E, 0x43, + 0xE7, 0x80, 0xB9, 0x43, 0xE7, 0x81, 0x8A, 0x43, + 0xE7, 0x81, 0xAB, 0x43, 0xE7, 0x81, 0xB0, 0x43, + 0xE7, 0x81, 0xB7, 0x43, 0xE7, 0x81, 0xBD, 0x43, + 0xE7, 0x82, 0x99, 0x43, 0xE7, 0x82, 0xAD, 0x43, + 0xE7, 0x83, 0x88, 0x43, 0xE7, 0x83, 0x99, 0x43, + 0xE7, 0x84, 0xA1, 0x43, 0xE7, 0x85, 0x85, 0x43, + 0xE7, 0x85, 0x89, 0x43, 0xE7, 0x85, 0xAE, 0x43, + // Bytes ec0 - eff + 0xE7, 0x86, 0x9C, 0x43, 0xE7, 0x87, 0x8E, 0x43, + 0xE7, 0x87, 0x90, 0x43, 0xE7, 0x88, 0x90, 0x43, + 0xE7, 0x88, 0x9B, 0x43, 0xE7, 0x88, 0xA8, 0x43, + 0xE7, 0x88, 0xAA, 0x43, 0xE7, 0x88, 0xAB, 0x43, + 0xE7, 0x88, 0xB5, 0x43, 0xE7, 0x88, 0xB6, 0x43, + 0xE7, 0x88, 0xBB, 0x43, 0xE7, 0x88, 0xBF, 0x43, + 0xE7, 0x89, 0x87, 0x43, 0xE7, 0x89, 0x90, 0x43, + 0xE7, 0x89, 0x99, 0x43, 0xE7, 0x89, 0x9B, 0x43, + // Bytes f00 - f3f + 0xE7, 0x89, 0xA2, 0x43, 0xE7, 0x89, 0xB9, 0x43, + 0xE7, 0x8A, 0x80, 0x43, 0xE7, 0x8A, 0x95, 0x43, + 0xE7, 0x8A, 0xAC, 0x43, 0xE7, 0x8A, 0xAF, 0x43, + 0xE7, 0x8B, 0x80, 0x43, 0xE7, 0x8B, 0xBC, 0x43, + 0xE7, 0x8C, 0xAA, 0x43, 0xE7, 0x8D, 0xB5, 0x43, + 0xE7, 0x8D, 0xBA, 0x43, 0xE7, 0x8E, 0x84, 0x43, + 0xE7, 0x8E, 0x87, 0x43, 0xE7, 0x8E, 0x89, 0x43, + 0xE7, 0x8E, 0x8B, 0x43, 0xE7, 0x8E, 0xA5, 0x43, + // Bytes f40 - f7f + 0xE7, 0x8E, 0xB2, 0x43, 0xE7, 0x8F, 0x9E, 0x43, + 0xE7, 0x90, 0x86, 0x43, 0xE7, 0x90, 0x89, 0x43, + 0xE7, 0x90, 0xA2, 0x43, 0xE7, 0x91, 0x87, 0x43, + 0xE7, 0x91, 0x9C, 0x43, 0xE7, 0x91, 0xA9, 0x43, + 0xE7, 0x91, 0xB1, 0x43, 0xE7, 0x92, 0x85, 0x43, + 0xE7, 0x92, 0x89, 0x43, 0xE7, 0x92, 0x98, 0x43, + 0xE7, 0x93, 0x8A, 0x43, 0xE7, 0x93, 0x9C, 0x43, + 0xE7, 0x93, 0xA6, 0x43, 0xE7, 0x94, 0x86, 0x43, + // Bytes f80 - fbf + 0xE7, 0x94, 0x98, 0x43, 0xE7, 0x94, 0x9F, 0x43, + 0xE7, 0x94, 0xA4, 0x43, 0xE7, 0x94, 0xA8, 0x43, + 0xE7, 0x94, 0xB0, 0x43, 0xE7, 0x94, 0xB2, 0x43, + 0xE7, 0x94, 0xB3, 0x43, 0xE7, 0x94, 0xB7, 0x43, + 0xE7, 0x94, 0xBB, 0x43, 0xE7, 0x94, 0xBE, 0x43, + 0xE7, 0x95, 0x99, 0x43, 0xE7, 0x95, 0xA5, 0x43, + 0xE7, 0x95, 0xB0, 0x43, 0xE7, 0x96, 0x8B, 0x43, + 0xE7, 0x96, 0x92, 0x43, 0xE7, 0x97, 0xA2, 0x43, + // Bytes fc0 - fff + 0xE7, 0x98, 0x90, 0x43, 0xE7, 0x98, 0x9D, 0x43, + 0xE7, 0x98, 0x9F, 0x43, 0xE7, 0x99, 0x82, 0x43, + 0xE7, 0x99, 0xA9, 0x43, 0xE7, 0x99, 0xB6, 0x43, + 0xE7, 0x99, 0xBD, 0x43, 0xE7, 0x9A, 0xAE, 0x43, + 0xE7, 0x9A, 0xBF, 0x43, 0xE7, 0x9B, 0x8A, 0x43, + 0xE7, 0x9B, 0x9B, 0x43, 0xE7, 0x9B, 0xA3, 0x43, + 0xE7, 0x9B, 0xA7, 0x43, 0xE7, 0x9B, 0xAE, 0x43, + 0xE7, 0x9B, 0xB4, 0x43, 0xE7, 0x9C, 0x81, 0x43, + // Bytes 1000 - 103f + 0xE7, 0x9C, 0x9E, 0x43, 0xE7, 0x9C, 0x9F, 0x43, + 0xE7, 0x9D, 0x80, 0x43, 0xE7, 0x9D, 0x8A, 0x43, + 0xE7, 0x9E, 0x8B, 0x43, 0xE7, 0x9E, 0xA7, 0x43, + 0xE7, 0x9F, 0x9B, 0x43, 0xE7, 0x9F, 0xA2, 0x43, + 0xE7, 0x9F, 0xB3, 0x43, 0xE7, 0xA1, 0x8E, 0x43, + 0xE7, 0xA1, 0xAB, 0x43, 0xE7, 0xA2, 0x8C, 0x43, + 0xE7, 0xA2, 0x91, 0x43, 0xE7, 0xA3, 0x8A, 0x43, + 0xE7, 0xA3, 0x8C, 0x43, 0xE7, 0xA3, 0xBB, 0x43, + // Bytes 1040 - 107f + 0xE7, 0xA4, 0xAA, 0x43, 0xE7, 0xA4, 0xBA, 0x43, + 0xE7, 0xA4, 0xBC, 0x43, 0xE7, 0xA4, 0xBE, 0x43, + 0xE7, 0xA5, 0x88, 0x43, 0xE7, 0xA5, 0x89, 0x43, + 0xE7, 0xA5, 0x90, 0x43, 0xE7, 0xA5, 0x96, 0x43, + 0xE7, 0xA5, 0x9D, 0x43, 0xE7, 0xA5, 0x9E, 0x43, + 0xE7, 0xA5, 0xA5, 0x43, 0xE7, 0xA5, 0xBF, 0x43, + 0xE7, 0xA6, 0x81, 0x43, 0xE7, 0xA6, 0x8D, 0x43, + 0xE7, 0xA6, 0x8E, 0x43, 0xE7, 0xA6, 0x8F, 0x43, + // Bytes 1080 - 10bf + 0xE7, 0xA6, 0xAE, 0x43, 0xE7, 0xA6, 0xB8, 0x43, + 0xE7, 0xA6, 0xBE, 0x43, 0xE7, 0xA7, 0x8A, 0x43, + 0xE7, 0xA7, 0x98, 0x43, 0xE7, 0xA7, 0xAB, 0x43, + 0xE7, 0xA8, 0x9C, 0x43, 0xE7, 0xA9, 0x80, 0x43, + 0xE7, 0xA9, 0x8A, 0x43, 0xE7, 0xA9, 0x8F, 0x43, + 0xE7, 0xA9, 0xB4, 0x43, 0xE7, 0xA9, 0xBA, 0x43, + 0xE7, 0xAA, 0x81, 0x43, 0xE7, 0xAA, 0xB1, 0x43, + 0xE7, 0xAB, 0x8B, 0x43, 0xE7, 0xAB, 0xAE, 0x43, + // Bytes 10c0 - 10ff + 0xE7, 0xAB, 0xB9, 0x43, 0xE7, 0xAC, 0xA0, 0x43, + 0xE7, 0xAE, 0x8F, 0x43, 0xE7, 0xAF, 0x80, 0x43, + 0xE7, 0xAF, 0x86, 0x43, 0xE7, 0xAF, 0x89, 0x43, + 0xE7, 0xB0, 0xBE, 0x43, 0xE7, 0xB1, 0xA0, 0x43, + 0xE7, 0xB1, 0xB3, 0x43, 0xE7, 0xB1, 0xBB, 0x43, + 0xE7, 0xB2, 0x92, 0x43, 0xE7, 0xB2, 0xBE, 0x43, + 0xE7, 0xB3, 0x92, 0x43, 0xE7, 0xB3, 0x96, 0x43, + 0xE7, 0xB3, 0xA3, 0x43, 0xE7, 0xB3, 0xA7, 0x43, + // Bytes 1100 - 113f + 0xE7, 0xB3, 0xA8, 0x43, 0xE7, 0xB3, 0xB8, 0x43, + 0xE7, 0xB4, 0x80, 0x43, 0xE7, 0xB4, 0x90, 0x43, + 0xE7, 0xB4, 0xA2, 0x43, 0xE7, 0xB4, 0xAF, 0x43, + 0xE7, 0xB5, 0x82, 0x43, 0xE7, 0xB5, 0x9B, 0x43, + 0xE7, 0xB5, 0xA3, 0x43, 0xE7, 0xB6, 0xA0, 0x43, + 0xE7, 0xB6, 0xBE, 0x43, 0xE7, 0xB7, 0x87, 0x43, + 0xE7, 0xB7, 0xB4, 0x43, 0xE7, 0xB8, 0x82, 0x43, + 0xE7, 0xB8, 0x89, 0x43, 0xE7, 0xB8, 0xB7, 0x43, + // Bytes 1140 - 117f + 0xE7, 0xB9, 0x81, 0x43, 0xE7, 0xB9, 0x85, 0x43, + 0xE7, 0xBC, 0xB6, 0x43, 0xE7, 0xBC, 0xBE, 0x43, + 0xE7, 0xBD, 0x91, 0x43, 0xE7, 0xBD, 0xB2, 0x43, + 0xE7, 0xBD, 0xB9, 0x43, 0xE7, 0xBD, 0xBA, 0x43, + 0xE7, 0xBE, 0x85, 0x43, 0xE7, 0xBE, 0x8A, 0x43, + 0xE7, 0xBE, 0x95, 0x43, 0xE7, 0xBE, 0x9A, 0x43, + 0xE7, 0xBE, 0xBD, 0x43, 0xE7, 0xBF, 0xBA, 0x43, + 0xE8, 0x80, 0x81, 0x43, 0xE8, 0x80, 0x85, 0x43, + // Bytes 1180 - 11bf + 0xE8, 0x80, 0x8C, 0x43, 0xE8, 0x80, 0x92, 0x43, + 0xE8, 0x80, 0xB3, 0x43, 0xE8, 0x81, 0x86, 0x43, + 0xE8, 0x81, 0xA0, 0x43, 0xE8, 0x81, 0xAF, 0x43, + 0xE8, 0x81, 0xB0, 0x43, 0xE8, 0x81, 0xBE, 0x43, + 0xE8, 0x81, 0xBF, 0x43, 0xE8, 0x82, 0x89, 0x43, + 0xE8, 0x82, 0x8B, 0x43, 0xE8, 0x82, 0xAD, 0x43, + 0xE8, 0x82, 0xB2, 0x43, 0xE8, 0x84, 0x83, 0x43, + 0xE8, 0x84, 0xBE, 0x43, 0xE8, 0x87, 0x98, 0x43, + // Bytes 11c0 - 11ff + 0xE8, 0x87, 0xA3, 0x43, 0xE8, 0x87, 0xA8, 0x43, + 0xE8, 0x87, 0xAA, 0x43, 0xE8, 0x87, 0xAD, 0x43, + 0xE8, 0x87, 0xB3, 0x43, 0xE8, 0x87, 0xBC, 0x43, + 0xE8, 0x88, 0x81, 0x43, 0xE8, 0x88, 0x84, 0x43, + 0xE8, 0x88, 0x8C, 0x43, 0xE8, 0x88, 0x98, 0x43, + 0xE8, 0x88, 0x9B, 0x43, 0xE8, 0x88, 0x9F, 0x43, + 0xE8, 0x89, 0xAE, 0x43, 0xE8, 0x89, 0xAF, 0x43, + 0xE8, 0x89, 0xB2, 0x43, 0xE8, 0x89, 0xB8, 0x43, + // Bytes 1200 - 123f + 0xE8, 0x89, 0xB9, 0x43, 0xE8, 0x8A, 0x8B, 0x43, + 0xE8, 0x8A, 0x91, 0x43, 0xE8, 0x8A, 0x9D, 0x43, + 0xE8, 0x8A, 0xB1, 0x43, 0xE8, 0x8A, 0xB3, 0x43, + 0xE8, 0x8A, 0xBD, 0x43, 0xE8, 0x8B, 0xA5, 0x43, + 0xE8, 0x8B, 0xA6, 0x43, 0xE8, 0x8C, 0x9D, 0x43, + 0xE8, 0x8C, 0xA3, 0x43, 0xE8, 0x8C, 0xB6, 0x43, + 0xE8, 0x8D, 0x92, 0x43, 0xE8, 0x8D, 0x93, 0x43, + 0xE8, 0x8D, 0xA3, 0x43, 0xE8, 0x8E, 0xAD, 0x43, + // Bytes 1240 - 127f + 0xE8, 0x8E, 0xBD, 0x43, 0xE8, 0x8F, 0x89, 0x43, + 0xE8, 0x8F, 0x8A, 0x43, 0xE8, 0x8F, 0x8C, 0x43, + 0xE8, 0x8F, 0x9C, 0x43, 0xE8, 0x8F, 0xA7, 0x43, + 0xE8, 0x8F, 0xAF, 0x43, 0xE8, 0x8F, 0xB1, 0x43, + 0xE8, 0x90, 0xBD, 0x43, 0xE8, 0x91, 0x89, 0x43, + 0xE8, 0x91, 0x97, 0x43, 0xE8, 0x93, 0xAE, 0x43, + 0xE8, 0x93, 0xB1, 0x43, 0xE8, 0x93, 0xB3, 0x43, + 0xE8, 0x93, 0xBC, 0x43, 0xE8, 0x94, 0x96, 0x43, + // Bytes 1280 - 12bf + 0xE8, 0x95, 0xA4, 0x43, 0xE8, 0x97, 0x8D, 0x43, + 0xE8, 0x97, 0xBA, 0x43, 0xE8, 0x98, 0x86, 0x43, + 0xE8, 0x98, 0x92, 0x43, 0xE8, 0x98, 0xAD, 0x43, + 0xE8, 0x98, 0xBF, 0x43, 0xE8, 0x99, 0x8D, 0x43, + 0xE8, 0x99, 0x90, 0x43, 0xE8, 0x99, 0x9C, 0x43, + 0xE8, 0x99, 0xA7, 0x43, 0xE8, 0x99, 0xA9, 0x43, + 0xE8, 0x99, 0xAB, 0x43, 0xE8, 0x9A, 0x88, 0x43, + 0xE8, 0x9A, 0xA9, 0x43, 0xE8, 0x9B, 0xA2, 0x43, + // Bytes 12c0 - 12ff + 0xE8, 0x9C, 0x8E, 0x43, 0xE8, 0x9C, 0xA8, 0x43, + 0xE8, 0x9D, 0xAB, 0x43, 0xE8, 0x9D, 0xB9, 0x43, + 0xE8, 0x9E, 0x86, 0x43, 0xE8, 0x9E, 0xBA, 0x43, + 0xE8, 0x9F, 0xA1, 0x43, 0xE8, 0xA0, 0x81, 0x43, + 0xE8, 0xA0, 0x9F, 0x43, 0xE8, 0xA1, 0x80, 0x43, + 0xE8, 0xA1, 0x8C, 0x43, 0xE8, 0xA1, 0xA0, 0x43, + 0xE8, 0xA1, 0xA3, 0x43, 0xE8, 0xA3, 0x82, 0x43, + 0xE8, 0xA3, 0x8F, 0x43, 0xE8, 0xA3, 0x97, 0x43, + // Bytes 1300 - 133f + 0xE8, 0xA3, 0x9E, 0x43, 0xE8, 0xA3, 0xA1, 0x43, + 0xE8, 0xA3, 0xB8, 0x43, 0xE8, 0xA3, 0xBA, 0x43, + 0xE8, 0xA4, 0x90, 0x43, 0xE8, 0xA5, 0x81, 0x43, + 0xE8, 0xA5, 0xA4, 0x43, 0xE8, 0xA5, 0xBE, 0x43, + 0xE8, 0xA6, 0x86, 0x43, 0xE8, 0xA6, 0x8B, 0x43, + 0xE8, 0xA6, 0x96, 0x43, 0xE8, 0xA7, 0x92, 0x43, + 0xE8, 0xA7, 0xA3, 0x43, 0xE8, 0xA8, 0x80, 0x43, + 0xE8, 0xAA, 0xA0, 0x43, 0xE8, 0xAA, 0xAA, 0x43, + // Bytes 1340 - 137f + 0xE8, 0xAA, 0xBF, 0x43, 0xE8, 0xAB, 0x8B, 0x43, + 0xE8, 0xAB, 0x92, 0x43, 0xE8, 0xAB, 0x96, 0x43, + 0xE8, 0xAB, 0xAD, 0x43, 0xE8, 0xAB, 0xB8, 0x43, + 0xE8, 0xAB, 0xBE, 0x43, 0xE8, 0xAC, 0x81, 0x43, + 0xE8, 0xAC, 0xB9, 0x43, 0xE8, 0xAD, 0x98, 0x43, + 0xE8, 0xAE, 0x80, 0x43, 0xE8, 0xAE, 0x8A, 0x43, + 0xE8, 0xB0, 0xB7, 0x43, 0xE8, 0xB1, 0x86, 0x43, + 0xE8, 0xB1, 0x88, 0x43, 0xE8, 0xB1, 0x95, 0x43, + // Bytes 1380 - 13bf + 0xE8, 0xB1, 0xB8, 0x43, 0xE8, 0xB2, 0x9D, 0x43, + 0xE8, 0xB2, 0xA1, 0x43, 0xE8, 0xB2, 0xA9, 0x43, + 0xE8, 0xB2, 0xAB, 0x43, 0xE8, 0xB3, 0x81, 0x43, + 0xE8, 0xB3, 0x82, 0x43, 0xE8, 0xB3, 0x87, 0x43, + 0xE8, 0xB3, 0x88, 0x43, 0xE8, 0xB3, 0x93, 0x43, + 0xE8, 0xB4, 0x88, 0x43, 0xE8, 0xB4, 0x9B, 0x43, + 0xE8, 0xB5, 0xA4, 0x43, 0xE8, 0xB5, 0xB0, 0x43, + 0xE8, 0xB5, 0xB7, 0x43, 0xE8, 0xB6, 0xB3, 0x43, + // Bytes 13c0 - 13ff + 0xE8, 0xB6, 0xBC, 0x43, 0xE8, 0xB7, 0x8B, 0x43, + 0xE8, 0xB7, 0xAF, 0x43, 0xE8, 0xB7, 0xB0, 0x43, + 0xE8, 0xBA, 0xAB, 0x43, 0xE8, 0xBB, 0x8A, 0x43, + 0xE8, 0xBB, 0x94, 0x43, 0xE8, 0xBC, 0xA6, 0x43, + 0xE8, 0xBC, 0xAA, 0x43, 0xE8, 0xBC, 0xB8, 0x43, + 0xE8, 0xBC, 0xBB, 0x43, 0xE8, 0xBD, 0xA2, 0x43, + 0xE8, 0xBE, 0x9B, 0x43, 0xE8, 0xBE, 0x9E, 0x43, + 0xE8, 0xBE, 0xB0, 0x43, 0xE8, 0xBE, 0xB5, 0x43, + // Bytes 1400 - 143f + 0xE8, 0xBE, 0xB6, 0x43, 0xE9, 0x80, 0xA3, 0x43, + 0xE9, 0x80, 0xB8, 0x43, 0xE9, 0x81, 0x8A, 0x43, + 0xE9, 0x81, 0xA9, 0x43, 0xE9, 0x81, 0xB2, 0x43, + 0xE9, 0x81, 0xBC, 0x43, 0xE9, 0x82, 0x8F, 0x43, + 0xE9, 0x82, 0x91, 0x43, 0xE9, 0x82, 0x94, 0x43, + 0xE9, 0x83, 0x8E, 0x43, 0xE9, 0x83, 0x9E, 0x43, + 0xE9, 0x83, 0xB1, 0x43, 0xE9, 0x83, 0xBD, 0x43, + 0xE9, 0x84, 0x91, 0x43, 0xE9, 0x84, 0x9B, 0x43, + // Bytes 1440 - 147f + 0xE9, 0x85, 0x89, 0x43, 0xE9, 0x85, 0xAA, 0x43, + 0xE9, 0x86, 0x99, 0x43, 0xE9, 0x86, 0xB4, 0x43, + 0xE9, 0x87, 0x86, 0x43, 0xE9, 0x87, 0x8C, 0x43, + 0xE9, 0x87, 0x8F, 0x43, 0xE9, 0x87, 0x91, 0x43, + 0xE9, 0x88, 0xB4, 0x43, 0xE9, 0x88, 0xB8, 0x43, + 0xE9, 0x89, 0xB6, 0x43, 0xE9, 0x89, 0xBC, 0x43, + 0xE9, 0x8B, 0x97, 0x43, 0xE9, 0x8B, 0x98, 0x43, + 0xE9, 0x8C, 0x84, 0x43, 0xE9, 0x8D, 0x8A, 0x43, + // Bytes 1480 - 14bf + 0xE9, 0x8F, 0xB9, 0x43, 0xE9, 0x90, 0x95, 0x43, + 0xE9, 0x95, 0xB7, 0x43, 0xE9, 0x96, 0x80, 0x43, + 0xE9, 0x96, 0x8B, 0x43, 0xE9, 0x96, 0xAD, 0x43, + 0xE9, 0x96, 0xB7, 0x43, 0xE9, 0x98, 0x9C, 0x43, + 0xE9, 0x98, 0xAE, 0x43, 0xE9, 0x99, 0x8B, 0x43, + 0xE9, 0x99, 0x8D, 0x43, 0xE9, 0x99, 0xB5, 0x43, + 0xE9, 0x99, 0xB8, 0x43, 0xE9, 0x99, 0xBC, 0x43, + 0xE9, 0x9A, 0x86, 0x43, 0xE9, 0x9A, 0xA3, 0x43, + // Bytes 14c0 - 14ff + 0xE9, 0x9A, 0xB6, 0x43, 0xE9, 0x9A, 0xB7, 0x43, + 0xE9, 0x9A, 0xB8, 0x43, 0xE9, 0x9A, 0xB9, 0x43, + 0xE9, 0x9B, 0x83, 0x43, 0xE9, 0x9B, 0xA2, 0x43, + 0xE9, 0x9B, 0xA3, 0x43, 0xE9, 0x9B, 0xA8, 0x43, + 0xE9, 0x9B, 0xB6, 0x43, 0xE9, 0x9B, 0xB7, 0x43, + 0xE9, 0x9C, 0xA3, 0x43, 0xE9, 0x9C, 0xB2, 0x43, + 0xE9, 0x9D, 0x88, 0x43, 0xE9, 0x9D, 0x91, 0x43, + 0xE9, 0x9D, 0x96, 0x43, 0xE9, 0x9D, 0x9E, 0x43, + // Bytes 1500 - 153f + 0xE9, 0x9D, 0xA2, 0x43, 0xE9, 0x9D, 0xA9, 0x43, + 0xE9, 0x9F, 0x8B, 0x43, 0xE9, 0x9F, 0x9B, 0x43, + 0xE9, 0x9F, 0xA0, 0x43, 0xE9, 0x9F, 0xAD, 0x43, + 0xE9, 0x9F, 0xB3, 0x43, 0xE9, 0x9F, 0xBF, 0x43, + 0xE9, 0xA0, 0x81, 0x43, 0xE9, 0xA0, 0x85, 0x43, + 0xE9, 0xA0, 0x8B, 0x43, 0xE9, 0xA0, 0x98, 0x43, + 0xE9, 0xA0, 0xA9, 0x43, 0xE9, 0xA0, 0xBB, 0x43, + 0xE9, 0xA1, 0x9E, 0x43, 0xE9, 0xA2, 0xA8, 0x43, + // Bytes 1540 - 157f + 0xE9, 0xA3, 0x9B, 0x43, 0xE9, 0xA3, 0x9F, 0x43, + 0xE9, 0xA3, 0xA2, 0x43, 0xE9, 0xA3, 0xAF, 0x43, + 0xE9, 0xA3, 0xBC, 0x43, 0xE9, 0xA4, 0xA8, 0x43, + 0xE9, 0xA4, 0xA9, 0x43, 0xE9, 0xA6, 0x96, 0x43, + 0xE9, 0xA6, 0x99, 0x43, 0xE9, 0xA6, 0xA7, 0x43, + 0xE9, 0xA6, 0xAC, 0x43, 0xE9, 0xA7, 0x82, 0x43, + 0xE9, 0xA7, 0xB1, 0x43, 0xE9, 0xA7, 0xBE, 0x43, + 0xE9, 0xA9, 0xAA, 0x43, 0xE9, 0xAA, 0xA8, 0x43, + // Bytes 1580 - 15bf + 0xE9, 0xAB, 0x98, 0x43, 0xE9, 0xAB, 0x9F, 0x43, + 0xE9, 0xAC, 0x92, 0x43, 0xE9, 0xAC, 0xA5, 0x43, + 0xE9, 0xAC, 0xAF, 0x43, 0xE9, 0xAC, 0xB2, 0x43, + 0xE9, 0xAC, 0xBC, 0x43, 0xE9, 0xAD, 0x9A, 0x43, + 0xE9, 0xAD, 0xAF, 0x43, 0xE9, 0xB1, 0x80, 0x43, + 0xE9, 0xB1, 0x97, 0x43, 0xE9, 0xB3, 0xA5, 0x43, + 0xE9, 0xB3, 0xBD, 0x43, 0xE9, 0xB5, 0xA7, 0x43, + 0xE9, 0xB6, 0xB4, 0x43, 0xE9, 0xB7, 0xBA, 0x43, + // Bytes 15c0 - 15ff + 0xE9, 0xB8, 0x9E, 0x43, 0xE9, 0xB9, 0xB5, 0x43, + 0xE9, 0xB9, 0xBF, 0x43, 0xE9, 0xBA, 0x97, 0x43, + 0xE9, 0xBA, 0x9F, 0x43, 0xE9, 0xBA, 0xA5, 0x43, + 0xE9, 0xBA, 0xBB, 0x43, 0xE9, 0xBB, 0x83, 0x43, + 0xE9, 0xBB, 0x8D, 0x43, 0xE9, 0xBB, 0x8E, 0x43, + 0xE9, 0xBB, 0x91, 0x43, 0xE9, 0xBB, 0xB9, 0x43, + 0xE9, 0xBB, 0xBD, 0x43, 0xE9, 0xBB, 0xBE, 0x43, + 0xE9, 0xBC, 0x85, 0x43, 0xE9, 0xBC, 0x8E, 0x43, + // Bytes 1600 - 163f + 0xE9, 0xBC, 0x8F, 0x43, 0xE9, 0xBC, 0x93, 0x43, + 0xE9, 0xBC, 0x96, 0x43, 0xE9, 0xBC, 0xA0, 0x43, + 0xE9, 0xBC, 0xBB, 0x43, 0xE9, 0xBD, 0x83, 0x43, + 0xE9, 0xBD, 0x8A, 0x43, 0xE9, 0xBD, 0x92, 0x43, + 0xE9, 0xBE, 0x8D, 0x43, 0xE9, 0xBE, 0x8E, 0x43, + 0xE9, 0xBE, 0x9C, 0x43, 0xE9, 0xBE, 0x9F, 0x43, + 0xE9, 0xBE, 0xA0, 0x43, 0xEA, 0x9C, 0xA7, 0x43, + 0xEA, 0x9D, 0xAF, 0x43, 0xEA, 0xAC, 0xB7, 0x43, + // Bytes 1640 - 167f + 0xEA, 0xAD, 0x92, 0x44, 0xF0, 0xA0, 0x84, 0xA2, + 0x44, 0xF0, 0xA0, 0x94, 0x9C, 0x44, 0xF0, 0xA0, + 0x94, 0xA5, 0x44, 0xF0, 0xA0, 0x95, 0x8B, 0x44, + 0xF0, 0xA0, 0x98, 0xBA, 0x44, 0xF0, 0xA0, 0xA0, + 0x84, 0x44, 0xF0, 0xA0, 0xA3, 0x9E, 0x44, 0xF0, + 0xA0, 0xA8, 0xAC, 0x44, 0xF0, 0xA0, 0xAD, 0xA3, + 0x44, 0xF0, 0xA1, 0x93, 0xA4, 0x44, 0xF0, 0xA1, + 0x9A, 0xA8, 0x44, 0xF0, 0xA1, 0x9B, 0xAA, 0x44, + // Bytes 1680 - 16bf + 0xF0, 0xA1, 0xA7, 0x88, 0x44, 0xF0, 0xA1, 0xAC, + 0x98, 0x44, 0xF0, 0xA1, 0xB4, 0x8B, 0x44, 0xF0, + 0xA1, 0xB7, 0xA4, 0x44, 0xF0, 0xA1, 0xB7, 0xA6, + 0x44, 0xF0, 0xA2, 0x86, 0x83, 0x44, 0xF0, 0xA2, + 0x86, 0x9F, 0x44, 0xF0, 0xA2, 0x8C, 0xB1, 0x44, + 0xF0, 0xA2, 0x9B, 0x94, 0x44, 0xF0, 0xA2, 0xA1, + 0x84, 0x44, 0xF0, 0xA2, 0xA1, 0x8A, 0x44, 0xF0, + 0xA2, 0xAC, 0x8C, 0x44, 0xF0, 0xA2, 0xAF, 0xB1, + // Bytes 16c0 - 16ff + 0x44, 0xF0, 0xA3, 0x80, 0x8A, 0x44, 0xF0, 0xA3, + 0x8A, 0xB8, 0x44, 0xF0, 0xA3, 0x8D, 0x9F, 0x44, + 0xF0, 0xA3, 0x8E, 0x93, 0x44, 0xF0, 0xA3, 0x8E, + 0x9C, 0x44, 0xF0, 0xA3, 0x8F, 0x83, 0x44, 0xF0, + 0xA3, 0x8F, 0x95, 0x44, 0xF0, 0xA3, 0x91, 0xAD, + 0x44, 0xF0, 0xA3, 0x9A, 0xA3, 0x44, 0xF0, 0xA3, + 0xA2, 0xA7, 0x44, 0xF0, 0xA3, 0xAA, 0x8D, 0x44, + 0xF0, 0xA3, 0xAB, 0xBA, 0x44, 0xF0, 0xA3, 0xB2, + // Bytes 1700 - 173f + 0xBC, 0x44, 0xF0, 0xA3, 0xB4, 0x9E, 0x44, 0xF0, + 0xA3, 0xBB, 0x91, 0x44, 0xF0, 0xA3, 0xBD, 0x9E, + 0x44, 0xF0, 0xA3, 0xBE, 0x8E, 0x44, 0xF0, 0xA4, + 0x89, 0xA3, 0x44, 0xF0, 0xA4, 0x8B, 0xAE, 0x44, + 0xF0, 0xA4, 0x8E, 0xAB, 0x44, 0xF0, 0xA4, 0x98, + 0x88, 0x44, 0xF0, 0xA4, 0x9C, 0xB5, 0x44, 0xF0, + 0xA4, 0xA0, 0x94, 0x44, 0xF0, 0xA4, 0xB0, 0xB6, + 0x44, 0xF0, 0xA4, 0xB2, 0x92, 0x44, 0xF0, 0xA4, + // Bytes 1740 - 177f + 0xBE, 0xA1, 0x44, 0xF0, 0xA4, 0xBE, 0xB8, 0x44, + 0xF0, 0xA5, 0x81, 0x84, 0x44, 0xF0, 0xA5, 0x83, + 0xB2, 0x44, 0xF0, 0xA5, 0x83, 0xB3, 0x44, 0xF0, + 0xA5, 0x84, 0x99, 0x44, 0xF0, 0xA5, 0x84, 0xB3, + 0x44, 0xF0, 0xA5, 0x89, 0x89, 0x44, 0xF0, 0xA5, + 0x90, 0x9D, 0x44, 0xF0, 0xA5, 0x98, 0xA6, 0x44, + 0xF0, 0xA5, 0x9A, 0x9A, 0x44, 0xF0, 0xA5, 0x9B, + 0x85, 0x44, 0xF0, 0xA5, 0xA5, 0xBC, 0x44, 0xF0, + // Bytes 1780 - 17bf + 0xA5, 0xAA, 0xA7, 0x44, 0xF0, 0xA5, 0xAE, 0xAB, + 0x44, 0xF0, 0xA5, 0xB2, 0x80, 0x44, 0xF0, 0xA5, + 0xB3, 0x90, 0x44, 0xF0, 0xA5, 0xBE, 0x86, 0x44, + 0xF0, 0xA6, 0x87, 0x9A, 0x44, 0xF0, 0xA6, 0x88, + 0xA8, 0x44, 0xF0, 0xA6, 0x89, 0x87, 0x44, 0xF0, + 0xA6, 0x8B, 0x99, 0x44, 0xF0, 0xA6, 0x8C, 0xBE, + 0x44, 0xF0, 0xA6, 0x93, 0x9A, 0x44, 0xF0, 0xA6, + 0x94, 0xA3, 0x44, 0xF0, 0xA6, 0x96, 0xA8, 0x44, + // Bytes 17c0 - 17ff + 0xF0, 0xA6, 0x9E, 0xA7, 0x44, 0xF0, 0xA6, 0x9E, + 0xB5, 0x44, 0xF0, 0xA6, 0xAC, 0xBC, 0x44, 0xF0, + 0xA6, 0xB0, 0xB6, 0x44, 0xF0, 0xA6, 0xB3, 0x95, + 0x44, 0xF0, 0xA6, 0xB5, 0xAB, 0x44, 0xF0, 0xA6, + 0xBC, 0xAC, 0x44, 0xF0, 0xA6, 0xBE, 0xB1, 0x44, + 0xF0, 0xA7, 0x83, 0x92, 0x44, 0xF0, 0xA7, 0x8F, + 0x8A, 0x44, 0xF0, 0xA7, 0x99, 0xA7, 0x44, 0xF0, + 0xA7, 0xA2, 0xAE, 0x44, 0xF0, 0xA7, 0xA5, 0xA6, + // Bytes 1800 - 183f + 0x44, 0xF0, 0xA7, 0xB2, 0xA8, 0x44, 0xF0, 0xA7, + 0xBB, 0x93, 0x44, 0xF0, 0xA7, 0xBC, 0xAF, 0x44, + 0xF0, 0xA8, 0x97, 0x92, 0x44, 0xF0, 0xA8, 0x97, + 0xAD, 0x44, 0xF0, 0xA8, 0x9C, 0xAE, 0x44, 0xF0, + 0xA8, 0xAF, 0xBA, 0x44, 0xF0, 0xA8, 0xB5, 0xB7, + 0x44, 0xF0, 0xA9, 0x85, 0x85, 0x44, 0xF0, 0xA9, + 0x87, 0x9F, 0x44, 0xF0, 0xA9, 0x88, 0x9A, 0x44, + 0xF0, 0xA9, 0x90, 0x8A, 0x44, 0xF0, 0xA9, 0x92, + // Bytes 1840 - 187f + 0x96, 0x44, 0xF0, 0xA9, 0x96, 0xB6, 0x44, 0xF0, + 0xA9, 0xAC, 0xB0, 0x44, 0xF0, 0xAA, 0x83, 0x8E, + 0x44, 0xF0, 0xAA, 0x84, 0x85, 0x44, 0xF0, 0xAA, + 0x88, 0x8E, 0x44, 0xF0, 0xAA, 0x8A, 0x91, 0x44, + 0xF0, 0xAA, 0x8E, 0x92, 0x44, 0xF0, 0xAA, 0x98, + 0x80, 0x42, 0x21, 0x21, 0x42, 0x21, 0x3F, 0x42, + 0x2E, 0x2E, 0x42, 0x30, 0x2C, 0x42, 0x30, 0x2E, + 0x42, 0x31, 0x2C, 0x42, 0x31, 0x2E, 0x42, 0x31, + // Bytes 1880 - 18bf + 0x30, 0x42, 0x31, 0x31, 0x42, 0x31, 0x32, 0x42, + 0x31, 0x33, 0x42, 0x31, 0x34, 0x42, 0x31, 0x35, + 0x42, 0x31, 0x36, 0x42, 0x31, 0x37, 0x42, 0x31, + 0x38, 0x42, 0x31, 0x39, 0x42, 0x32, 0x2C, 0x42, + 0x32, 0x2E, 0x42, 0x32, 0x30, 0x42, 0x32, 0x31, + 0x42, 0x32, 0x32, 0x42, 0x32, 0x33, 0x42, 0x32, + 0x34, 0x42, 0x32, 0x35, 0x42, 0x32, 0x36, 0x42, + 0x32, 0x37, 0x42, 0x32, 0x38, 0x42, 0x32, 0x39, + // Bytes 18c0 - 18ff + 0x42, 0x33, 0x2C, 0x42, 0x33, 0x2E, 0x42, 0x33, + 0x30, 0x42, 0x33, 0x31, 0x42, 0x33, 0x32, 0x42, + 0x33, 0x33, 0x42, 0x33, 0x34, 0x42, 0x33, 0x35, + 0x42, 0x33, 0x36, 0x42, 0x33, 0x37, 0x42, 0x33, + 0x38, 0x42, 0x33, 0x39, 0x42, 0x34, 0x2C, 0x42, + 0x34, 0x2E, 0x42, 0x34, 0x30, 0x42, 0x34, 0x31, + 0x42, 0x34, 0x32, 0x42, 0x34, 0x33, 0x42, 0x34, + 0x34, 0x42, 0x34, 0x35, 0x42, 0x34, 0x36, 0x42, + // Bytes 1900 - 193f + 0x34, 0x37, 0x42, 0x34, 0x38, 0x42, 0x34, 0x39, + 0x42, 0x35, 0x2C, 0x42, 0x35, 0x2E, 0x42, 0x35, + 0x30, 0x42, 0x36, 0x2C, 0x42, 0x36, 0x2E, 0x42, + 0x37, 0x2C, 0x42, 0x37, 0x2E, 0x42, 0x38, 0x2C, + 0x42, 0x38, 0x2E, 0x42, 0x39, 0x2C, 0x42, 0x39, + 0x2E, 0x42, 0x3D, 0x3D, 0x42, 0x3F, 0x21, 0x42, + 0x3F, 0x3F, 0x42, 0x41, 0x55, 0x42, 0x42, 0x71, + 0x42, 0x43, 0x44, 0x42, 0x44, 0x4A, 0x42, 0x44, + // Bytes 1940 - 197f + 0x5A, 0x42, 0x44, 0x7A, 0x42, 0x47, 0x42, 0x42, + 0x47, 0x79, 0x42, 0x48, 0x50, 0x42, 0x48, 0x56, + 0x42, 0x48, 0x67, 0x42, 0x48, 0x7A, 0x42, 0x49, + 0x49, 0x42, 0x49, 0x4A, 0x42, 0x49, 0x55, 0x42, + 0x49, 0x56, 0x42, 0x49, 0x58, 0x42, 0x4B, 0x42, + 0x42, 0x4B, 0x4B, 0x42, 0x4B, 0x4D, 0x42, 0x4C, + 0x4A, 0x42, 0x4C, 0x6A, 0x42, 0x4D, 0x42, 0x42, + 0x4D, 0x43, 0x42, 0x4D, 0x44, 0x42, 0x4D, 0x56, + // Bytes 1980 - 19bf + 0x42, 0x4D, 0x57, 0x42, 0x4E, 0x4A, 0x42, 0x4E, + 0x6A, 0x42, 0x4E, 0x6F, 0x42, 0x50, 0x48, 0x42, + 0x50, 0x52, 0x42, 0x50, 0x61, 0x42, 0x52, 0x73, + 0x42, 0x53, 0x44, 0x42, 0x53, 0x4D, 0x42, 0x53, + 0x53, 0x42, 0x53, 0x76, 0x42, 0x54, 0x4D, 0x42, + 0x56, 0x49, 0x42, 0x57, 0x43, 0x42, 0x57, 0x5A, + 0x42, 0x57, 0x62, 0x42, 0x58, 0x49, 0x42, 0x63, + 0x63, 0x42, 0x63, 0x64, 0x42, 0x63, 0x6D, 0x42, + // Bytes 19c0 - 19ff + 0x64, 0x42, 0x42, 0x64, 0x61, 0x42, 0x64, 0x6C, + 0x42, 0x64, 0x6D, 0x42, 0x64, 0x7A, 0x42, 0x65, + 0x56, 0x42, 0x66, 0x66, 0x42, 0x66, 0x69, 0x42, + 0x66, 0x6C, 0x42, 0x66, 0x6D, 0x42, 0x68, 0x61, + 0x42, 0x69, 0x69, 0x42, 0x69, 0x6A, 0x42, 0x69, + 0x6E, 0x42, 0x69, 0x76, 0x42, 0x69, 0x78, 0x42, + 0x6B, 0x41, 0x42, 0x6B, 0x56, 0x42, 0x6B, 0x57, + 0x42, 0x6B, 0x67, 0x42, 0x6B, 0x6C, 0x42, 0x6B, + // Bytes 1a00 - 1a3f + 0x6D, 0x42, 0x6B, 0x74, 0x42, 0x6C, 0x6A, 0x42, + 0x6C, 0x6D, 0x42, 0x6C, 0x6E, 0x42, 0x6C, 0x78, + 0x42, 0x6D, 0x32, 0x42, 0x6D, 0x33, 0x42, 0x6D, + 0x41, 0x42, 0x6D, 0x56, 0x42, 0x6D, 0x57, 0x42, + 0x6D, 0x62, 0x42, 0x6D, 0x67, 0x42, 0x6D, 0x6C, + 0x42, 0x6D, 0x6D, 0x42, 0x6D, 0x73, 0x42, 0x6E, + 0x41, 0x42, 0x6E, 0x46, 0x42, 0x6E, 0x56, 0x42, + 0x6E, 0x57, 0x42, 0x6E, 0x6A, 0x42, 0x6E, 0x6D, + // Bytes 1a40 - 1a7f + 0x42, 0x6E, 0x73, 0x42, 0x6F, 0x56, 0x42, 0x70, + 0x41, 0x42, 0x70, 0x46, 0x42, 0x70, 0x56, 0x42, + 0x70, 0x57, 0x42, 0x70, 0x63, 0x42, 0x70, 0x73, + 0x42, 0x73, 0x72, 0x42, 0x73, 0x74, 0x42, 0x76, + 0x69, 0x42, 0x78, 0x69, 0x43, 0x28, 0x31, 0x29, + 0x43, 0x28, 0x32, 0x29, 0x43, 0x28, 0x33, 0x29, + 0x43, 0x28, 0x34, 0x29, 0x43, 0x28, 0x35, 0x29, + 0x43, 0x28, 0x36, 0x29, 0x43, 0x28, 0x37, 0x29, + // Bytes 1a80 - 1abf + 0x43, 0x28, 0x38, 0x29, 0x43, 0x28, 0x39, 0x29, + 0x43, 0x28, 0x41, 0x29, 0x43, 0x28, 0x42, 0x29, + 0x43, 0x28, 0x43, 0x29, 0x43, 0x28, 0x44, 0x29, + 0x43, 0x28, 0x45, 0x29, 0x43, 0x28, 0x46, 0x29, + 0x43, 0x28, 0x47, 0x29, 0x43, 0x28, 0x48, 0x29, + 0x43, 0x28, 0x49, 0x29, 0x43, 0x28, 0x4A, 0x29, + 0x43, 0x28, 0x4B, 0x29, 0x43, 0x28, 0x4C, 0x29, + 0x43, 0x28, 0x4D, 0x29, 0x43, 0x28, 0x4E, 0x29, + // Bytes 1ac0 - 1aff + 0x43, 0x28, 0x4F, 0x29, 0x43, 0x28, 0x50, 0x29, + 0x43, 0x28, 0x51, 0x29, 0x43, 0x28, 0x52, 0x29, + 0x43, 0x28, 0x53, 0x29, 0x43, 0x28, 0x54, 0x29, + 0x43, 0x28, 0x55, 0x29, 0x43, 0x28, 0x56, 0x29, + 0x43, 0x28, 0x57, 0x29, 0x43, 0x28, 0x58, 0x29, + 0x43, 0x28, 0x59, 0x29, 0x43, 0x28, 0x5A, 0x29, + 0x43, 0x28, 0x61, 0x29, 0x43, 0x28, 0x62, 0x29, + 0x43, 0x28, 0x63, 0x29, 0x43, 0x28, 0x64, 0x29, + // Bytes 1b00 - 1b3f + 0x43, 0x28, 0x65, 0x29, 0x43, 0x28, 0x66, 0x29, + 0x43, 0x28, 0x67, 0x29, 0x43, 0x28, 0x68, 0x29, + 0x43, 0x28, 0x69, 0x29, 0x43, 0x28, 0x6A, 0x29, + 0x43, 0x28, 0x6B, 0x29, 0x43, 0x28, 0x6C, 0x29, + 0x43, 0x28, 0x6D, 0x29, 0x43, 0x28, 0x6E, 0x29, + 0x43, 0x28, 0x6F, 0x29, 0x43, 0x28, 0x70, 0x29, + 0x43, 0x28, 0x71, 0x29, 0x43, 0x28, 0x72, 0x29, + 0x43, 0x28, 0x73, 0x29, 0x43, 0x28, 0x74, 0x29, + // Bytes 1b40 - 1b7f + 0x43, 0x28, 0x75, 0x29, 0x43, 0x28, 0x76, 0x29, + 0x43, 0x28, 0x77, 0x29, 0x43, 0x28, 0x78, 0x29, + 0x43, 0x28, 0x79, 0x29, 0x43, 0x28, 0x7A, 0x29, + 0x43, 0x2E, 0x2E, 0x2E, 0x43, 0x31, 0x30, 0x2E, + 0x43, 0x31, 0x31, 0x2E, 0x43, 0x31, 0x32, 0x2E, + 0x43, 0x31, 0x33, 0x2E, 0x43, 0x31, 0x34, 0x2E, + 0x43, 0x31, 0x35, 0x2E, 0x43, 0x31, 0x36, 0x2E, + 0x43, 0x31, 0x37, 0x2E, 0x43, 0x31, 0x38, 0x2E, + // Bytes 1b80 - 1bbf + 0x43, 0x31, 0x39, 0x2E, 0x43, 0x32, 0x30, 0x2E, + 0x43, 0x3A, 0x3A, 0x3D, 0x43, 0x3D, 0x3D, 0x3D, + 0x43, 0x43, 0x6F, 0x2E, 0x43, 0x46, 0x41, 0x58, + 0x43, 0x47, 0x48, 0x7A, 0x43, 0x47, 0x50, 0x61, + 0x43, 0x49, 0x49, 0x49, 0x43, 0x4C, 0x54, 0x44, + 0x43, 0x4C, 0xC2, 0xB7, 0x43, 0x4D, 0x48, 0x7A, + 0x43, 0x4D, 0x50, 0x61, 0x43, 0x4D, 0xCE, 0xA9, + 0x43, 0x50, 0x50, 0x4D, 0x43, 0x50, 0x50, 0x56, + // Bytes 1bc0 - 1bff + 0x43, 0x50, 0x54, 0x45, 0x43, 0x54, 0x45, 0x4C, + 0x43, 0x54, 0x48, 0x7A, 0x43, 0x56, 0x49, 0x49, + 0x43, 0x58, 0x49, 0x49, 0x43, 0x61, 0x2F, 0x63, + 0x43, 0x61, 0x2F, 0x73, 0x43, 0x61, 0xCA, 0xBE, + 0x43, 0x62, 0x61, 0x72, 0x43, 0x63, 0x2F, 0x6F, + 0x43, 0x63, 0x2F, 0x75, 0x43, 0x63, 0x61, 0x6C, + 0x43, 0x63, 0x6D, 0x32, 0x43, 0x63, 0x6D, 0x33, + 0x43, 0x64, 0x6D, 0x32, 0x43, 0x64, 0x6D, 0x33, + // Bytes 1c00 - 1c3f + 0x43, 0x65, 0x72, 0x67, 0x43, 0x66, 0x66, 0x69, + 0x43, 0x66, 0x66, 0x6C, 0x43, 0x67, 0x61, 0x6C, + 0x43, 0x68, 0x50, 0x61, 0x43, 0x69, 0x69, 0x69, + 0x43, 0x6B, 0x48, 0x7A, 0x43, 0x6B, 0x50, 0x61, + 0x43, 0x6B, 0x6D, 0x32, 0x43, 0x6B, 0x6D, 0x33, + 0x43, 0x6B, 0xCE, 0xA9, 0x43, 0x6C, 0x6F, 0x67, + 0x43, 0x6C, 0xC2, 0xB7, 0x43, 0x6D, 0x69, 0x6C, + 0x43, 0x6D, 0x6D, 0x32, 0x43, 0x6D, 0x6D, 0x33, + // Bytes 1c40 - 1c7f + 0x43, 0x6D, 0x6F, 0x6C, 0x43, 0x72, 0x61, 0x64, + 0x43, 0x76, 0x69, 0x69, 0x43, 0x78, 0x69, 0x69, + 0x43, 0xC2, 0xB0, 0x43, 0x43, 0xC2, 0xB0, 0x46, + 0x43, 0xCA, 0xBC, 0x6E, 0x43, 0xCE, 0xBC, 0x41, + 0x43, 0xCE, 0xBC, 0x46, 0x43, 0xCE, 0xBC, 0x56, + 0x43, 0xCE, 0xBC, 0x57, 0x43, 0xCE, 0xBC, 0x67, + 0x43, 0xCE, 0xBC, 0x6C, 0x43, 0xCE, 0xBC, 0x6D, + 0x43, 0xCE, 0xBC, 0x73, 0x44, 0x28, 0x31, 0x30, + // Bytes 1c80 - 1cbf + 0x29, 0x44, 0x28, 0x31, 0x31, 0x29, 0x44, 0x28, + 0x31, 0x32, 0x29, 0x44, 0x28, 0x31, 0x33, 0x29, + 0x44, 0x28, 0x31, 0x34, 0x29, 0x44, 0x28, 0x31, + 0x35, 0x29, 0x44, 0x28, 0x31, 0x36, 0x29, 0x44, + 0x28, 0x31, 0x37, 0x29, 0x44, 0x28, 0x31, 0x38, + 0x29, 0x44, 0x28, 0x31, 0x39, 0x29, 0x44, 0x28, + 0x32, 0x30, 0x29, 0x44, 0x30, 0xE7, 0x82, 0xB9, + 0x44, 0x31, 0xE2, 0x81, 0x84, 0x44, 0x31, 0xE6, + // Bytes 1cc0 - 1cff + 0x97, 0xA5, 0x44, 0x31, 0xE6, 0x9C, 0x88, 0x44, + 0x31, 0xE7, 0x82, 0xB9, 0x44, 0x32, 0xE6, 0x97, + 0xA5, 0x44, 0x32, 0xE6, 0x9C, 0x88, 0x44, 0x32, + 0xE7, 0x82, 0xB9, 0x44, 0x33, 0xE6, 0x97, 0xA5, + 0x44, 0x33, 0xE6, 0x9C, 0x88, 0x44, 0x33, 0xE7, + 0x82, 0xB9, 0x44, 0x34, 0xE6, 0x97, 0xA5, 0x44, + 0x34, 0xE6, 0x9C, 0x88, 0x44, 0x34, 0xE7, 0x82, + 0xB9, 0x44, 0x35, 0xE6, 0x97, 0xA5, 0x44, 0x35, + // Bytes 1d00 - 1d3f + 0xE6, 0x9C, 0x88, 0x44, 0x35, 0xE7, 0x82, 0xB9, + 0x44, 0x36, 0xE6, 0x97, 0xA5, 0x44, 0x36, 0xE6, + 0x9C, 0x88, 0x44, 0x36, 0xE7, 0x82, 0xB9, 0x44, + 0x37, 0xE6, 0x97, 0xA5, 0x44, 0x37, 0xE6, 0x9C, + 0x88, 0x44, 0x37, 0xE7, 0x82, 0xB9, 0x44, 0x38, + 0xE6, 0x97, 0xA5, 0x44, 0x38, 0xE6, 0x9C, 0x88, + 0x44, 0x38, 0xE7, 0x82, 0xB9, 0x44, 0x39, 0xE6, + 0x97, 0xA5, 0x44, 0x39, 0xE6, 0x9C, 0x88, 0x44, + // Bytes 1d40 - 1d7f + 0x39, 0xE7, 0x82, 0xB9, 0x44, 0x56, 0x49, 0x49, + 0x49, 0x44, 0x61, 0x2E, 0x6D, 0x2E, 0x44, 0x6B, + 0x63, 0x61, 0x6C, 0x44, 0x70, 0x2E, 0x6D, 0x2E, + 0x44, 0x76, 0x69, 0x69, 0x69, 0x44, 0xD5, 0xA5, + 0xD6, 0x82, 0x44, 0xD5, 0xB4, 0xD5, 0xA5, 0x44, + 0xD5, 0xB4, 0xD5, 0xAB, 0x44, 0xD5, 0xB4, 0xD5, + 0xAD, 0x44, 0xD5, 0xB4, 0xD5, 0xB6, 0x44, 0xD5, + 0xBE, 0xD5, 0xB6, 0x44, 0xD7, 0x90, 0xD7, 0x9C, + // Bytes 1d80 - 1dbf + 0x44, 0xD8, 0xA7, 0xD9, 0xB4, 0x44, 0xD8, 0xA8, + 0xD8, 0xAC, 0x44, 0xD8, 0xA8, 0xD8, 0xAD, 0x44, + 0xD8, 0xA8, 0xD8, 0xAE, 0x44, 0xD8, 0xA8, 0xD8, + 0xB1, 0x44, 0xD8, 0xA8, 0xD8, 0xB2, 0x44, 0xD8, + 0xA8, 0xD9, 0x85, 0x44, 0xD8, 0xA8, 0xD9, 0x86, + 0x44, 0xD8, 0xA8, 0xD9, 0x87, 0x44, 0xD8, 0xA8, + 0xD9, 0x89, 0x44, 0xD8, 0xA8, 0xD9, 0x8A, 0x44, + 0xD8, 0xAA, 0xD8, 0xAC, 0x44, 0xD8, 0xAA, 0xD8, + // Bytes 1dc0 - 1dff + 0xAD, 0x44, 0xD8, 0xAA, 0xD8, 0xAE, 0x44, 0xD8, + 0xAA, 0xD8, 0xB1, 0x44, 0xD8, 0xAA, 0xD8, 0xB2, + 0x44, 0xD8, 0xAA, 0xD9, 0x85, 0x44, 0xD8, 0xAA, + 0xD9, 0x86, 0x44, 0xD8, 0xAA, 0xD9, 0x87, 0x44, + 0xD8, 0xAA, 0xD9, 0x89, 0x44, 0xD8, 0xAA, 0xD9, + 0x8A, 0x44, 0xD8, 0xAB, 0xD8, 0xAC, 0x44, 0xD8, + 0xAB, 0xD8, 0xB1, 0x44, 0xD8, 0xAB, 0xD8, 0xB2, + 0x44, 0xD8, 0xAB, 0xD9, 0x85, 0x44, 0xD8, 0xAB, + // Bytes 1e00 - 1e3f + 0xD9, 0x86, 0x44, 0xD8, 0xAB, 0xD9, 0x87, 0x44, + 0xD8, 0xAB, 0xD9, 0x89, 0x44, 0xD8, 0xAB, 0xD9, + 0x8A, 0x44, 0xD8, 0xAC, 0xD8, 0xAD, 0x44, 0xD8, + 0xAC, 0xD9, 0x85, 0x44, 0xD8, 0xAC, 0xD9, 0x89, + 0x44, 0xD8, 0xAC, 0xD9, 0x8A, 0x44, 0xD8, 0xAD, + 0xD8, 0xAC, 0x44, 0xD8, 0xAD, 0xD9, 0x85, 0x44, + 0xD8, 0xAD, 0xD9, 0x89, 0x44, 0xD8, 0xAD, 0xD9, + 0x8A, 0x44, 0xD8, 0xAE, 0xD8, 0xAC, 0x44, 0xD8, + // Bytes 1e40 - 1e7f + 0xAE, 0xD8, 0xAD, 0x44, 0xD8, 0xAE, 0xD9, 0x85, + 0x44, 0xD8, 0xAE, 0xD9, 0x89, 0x44, 0xD8, 0xAE, + 0xD9, 0x8A, 0x44, 0xD8, 0xB3, 0xD8, 0xAC, 0x44, + 0xD8, 0xB3, 0xD8, 0xAD, 0x44, 0xD8, 0xB3, 0xD8, + 0xAE, 0x44, 0xD8, 0xB3, 0xD8, 0xB1, 0x44, 0xD8, + 0xB3, 0xD9, 0x85, 0x44, 0xD8, 0xB3, 0xD9, 0x87, + 0x44, 0xD8, 0xB3, 0xD9, 0x89, 0x44, 0xD8, 0xB3, + 0xD9, 0x8A, 0x44, 0xD8, 0xB4, 0xD8, 0xAC, 0x44, + // Bytes 1e80 - 1ebf + 0xD8, 0xB4, 0xD8, 0xAD, 0x44, 0xD8, 0xB4, 0xD8, + 0xAE, 0x44, 0xD8, 0xB4, 0xD8, 0xB1, 0x44, 0xD8, + 0xB4, 0xD9, 0x85, 0x44, 0xD8, 0xB4, 0xD9, 0x87, + 0x44, 0xD8, 0xB4, 0xD9, 0x89, 0x44, 0xD8, 0xB4, + 0xD9, 0x8A, 0x44, 0xD8, 0xB5, 0xD8, 0xAD, 0x44, + 0xD8, 0xB5, 0xD8, 0xAE, 0x44, 0xD8, 0xB5, 0xD8, + 0xB1, 0x44, 0xD8, 0xB5, 0xD9, 0x85, 0x44, 0xD8, + 0xB5, 0xD9, 0x89, 0x44, 0xD8, 0xB5, 0xD9, 0x8A, + // Bytes 1ec0 - 1eff + 0x44, 0xD8, 0xB6, 0xD8, 0xAC, 0x44, 0xD8, 0xB6, + 0xD8, 0xAD, 0x44, 0xD8, 0xB6, 0xD8, 0xAE, 0x44, + 0xD8, 0xB6, 0xD8, 0xB1, 0x44, 0xD8, 0xB6, 0xD9, + 0x85, 0x44, 0xD8, 0xB6, 0xD9, 0x89, 0x44, 0xD8, + 0xB6, 0xD9, 0x8A, 0x44, 0xD8, 0xB7, 0xD8, 0xAD, + 0x44, 0xD8, 0xB7, 0xD9, 0x85, 0x44, 0xD8, 0xB7, + 0xD9, 0x89, 0x44, 0xD8, 0xB7, 0xD9, 0x8A, 0x44, + 0xD8, 0xB8, 0xD9, 0x85, 0x44, 0xD8, 0xB9, 0xD8, + // Bytes 1f00 - 1f3f + 0xAC, 0x44, 0xD8, 0xB9, 0xD9, 0x85, 0x44, 0xD8, + 0xB9, 0xD9, 0x89, 0x44, 0xD8, 0xB9, 0xD9, 0x8A, + 0x44, 0xD8, 0xBA, 0xD8, 0xAC, 0x44, 0xD8, 0xBA, + 0xD9, 0x85, 0x44, 0xD8, 0xBA, 0xD9, 0x89, 0x44, + 0xD8, 0xBA, 0xD9, 0x8A, 0x44, 0xD9, 0x81, 0xD8, + 0xAC, 0x44, 0xD9, 0x81, 0xD8, 0xAD, 0x44, 0xD9, + 0x81, 0xD8, 0xAE, 0x44, 0xD9, 0x81, 0xD9, 0x85, + 0x44, 0xD9, 0x81, 0xD9, 0x89, 0x44, 0xD9, 0x81, + // Bytes 1f40 - 1f7f + 0xD9, 0x8A, 0x44, 0xD9, 0x82, 0xD8, 0xAD, 0x44, + 0xD9, 0x82, 0xD9, 0x85, 0x44, 0xD9, 0x82, 0xD9, + 0x89, 0x44, 0xD9, 0x82, 0xD9, 0x8A, 0x44, 0xD9, + 0x83, 0xD8, 0xA7, 0x44, 0xD9, 0x83, 0xD8, 0xAC, + 0x44, 0xD9, 0x83, 0xD8, 0xAD, 0x44, 0xD9, 0x83, + 0xD8, 0xAE, 0x44, 0xD9, 0x83, 0xD9, 0x84, 0x44, + 0xD9, 0x83, 0xD9, 0x85, 0x44, 0xD9, 0x83, 0xD9, + 0x89, 0x44, 0xD9, 0x83, 0xD9, 0x8A, 0x44, 0xD9, + // Bytes 1f80 - 1fbf + 0x84, 0xD8, 0xA7, 0x44, 0xD9, 0x84, 0xD8, 0xAC, + 0x44, 0xD9, 0x84, 0xD8, 0xAD, 0x44, 0xD9, 0x84, + 0xD8, 0xAE, 0x44, 0xD9, 0x84, 0xD9, 0x85, 0x44, + 0xD9, 0x84, 0xD9, 0x87, 0x44, 0xD9, 0x84, 0xD9, + 0x89, 0x44, 0xD9, 0x84, 0xD9, 0x8A, 0x44, 0xD9, + 0x85, 0xD8, 0xA7, 0x44, 0xD9, 0x85, 0xD8, 0xAC, + 0x44, 0xD9, 0x85, 0xD8, 0xAD, 0x44, 0xD9, 0x85, + 0xD8, 0xAE, 0x44, 0xD9, 0x85, 0xD9, 0x85, 0x44, + // Bytes 1fc0 - 1fff + 0xD9, 0x85, 0xD9, 0x89, 0x44, 0xD9, 0x85, 0xD9, + 0x8A, 0x44, 0xD9, 0x86, 0xD8, 0xAC, 0x44, 0xD9, + 0x86, 0xD8, 0xAD, 0x44, 0xD9, 0x86, 0xD8, 0xAE, + 0x44, 0xD9, 0x86, 0xD8, 0xB1, 0x44, 0xD9, 0x86, + 0xD8, 0xB2, 0x44, 0xD9, 0x86, 0xD9, 0x85, 0x44, + 0xD9, 0x86, 0xD9, 0x86, 0x44, 0xD9, 0x86, 0xD9, + 0x87, 0x44, 0xD9, 0x86, 0xD9, 0x89, 0x44, 0xD9, + 0x86, 0xD9, 0x8A, 0x44, 0xD9, 0x87, 0xD8, 0xAC, + // Bytes 2000 - 203f + 0x44, 0xD9, 0x87, 0xD9, 0x85, 0x44, 0xD9, 0x87, + 0xD9, 0x89, 0x44, 0xD9, 0x87, 0xD9, 0x8A, 0x44, + 0xD9, 0x88, 0xD9, 0xB4, 0x44, 0xD9, 0x8A, 0xD8, + 0xAC, 0x44, 0xD9, 0x8A, 0xD8, 0xAD, 0x44, 0xD9, + 0x8A, 0xD8, 0xAE, 0x44, 0xD9, 0x8A, 0xD8, 0xB1, + 0x44, 0xD9, 0x8A, 0xD8, 0xB2, 0x44, 0xD9, 0x8A, + 0xD9, 0x85, 0x44, 0xD9, 0x8A, 0xD9, 0x86, 0x44, + 0xD9, 0x8A, 0xD9, 0x87, 0x44, 0xD9, 0x8A, 0xD9, + // Bytes 2040 - 207f + 0x89, 0x44, 0xD9, 0x8A, 0xD9, 0x8A, 0x44, 0xD9, + 0x8A, 0xD9, 0xB4, 0x44, 0xDB, 0x87, 0xD9, 0xB4, + 0x45, 0x28, 0xE1, 0x84, 0x80, 0x29, 0x45, 0x28, + 0xE1, 0x84, 0x82, 0x29, 0x45, 0x28, 0xE1, 0x84, + 0x83, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x85, 0x29, + 0x45, 0x28, 0xE1, 0x84, 0x86, 0x29, 0x45, 0x28, + 0xE1, 0x84, 0x87, 0x29, 0x45, 0x28, 0xE1, 0x84, + 0x89, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x8B, 0x29, + // Bytes 2080 - 20bf + 0x45, 0x28, 0xE1, 0x84, 0x8C, 0x29, 0x45, 0x28, + 0xE1, 0x84, 0x8E, 0x29, 0x45, 0x28, 0xE1, 0x84, + 0x8F, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x90, 0x29, + 0x45, 0x28, 0xE1, 0x84, 0x91, 0x29, 0x45, 0x28, + 0xE1, 0x84, 0x92, 0x29, 0x45, 0x28, 0xE4, 0xB8, + 0x80, 0x29, 0x45, 0x28, 0xE4, 0xB8, 0x83, 0x29, + 0x45, 0x28, 0xE4, 0xB8, 0x89, 0x29, 0x45, 0x28, + 0xE4, 0xB9, 0x9D, 0x29, 0x45, 0x28, 0xE4, 0xBA, + // Bytes 20c0 - 20ff + 0x8C, 0x29, 0x45, 0x28, 0xE4, 0xBA, 0x94, 0x29, + 0x45, 0x28, 0xE4, 0xBB, 0xA3, 0x29, 0x45, 0x28, + 0xE4, 0xBC, 0x81, 0x29, 0x45, 0x28, 0xE4, 0xBC, + 0x91, 0x29, 0x45, 0x28, 0xE5, 0x85, 0xAB, 0x29, + 0x45, 0x28, 0xE5, 0x85, 0xAD, 0x29, 0x45, 0x28, + 0xE5, 0x8A, 0xB4, 0x29, 0x45, 0x28, 0xE5, 0x8D, + 0x81, 0x29, 0x45, 0x28, 0xE5, 0x8D, 0x94, 0x29, + 0x45, 0x28, 0xE5, 0x90, 0x8D, 0x29, 0x45, 0x28, + // Bytes 2100 - 213f + 0xE5, 0x91, 0xBC, 0x29, 0x45, 0x28, 0xE5, 0x9B, + 0x9B, 0x29, 0x45, 0x28, 0xE5, 0x9C, 0x9F, 0x29, + 0x45, 0x28, 0xE5, 0xAD, 0xA6, 0x29, 0x45, 0x28, + 0xE6, 0x97, 0xA5, 0x29, 0x45, 0x28, 0xE6, 0x9C, + 0x88, 0x29, 0x45, 0x28, 0xE6, 0x9C, 0x89, 0x29, + 0x45, 0x28, 0xE6, 0x9C, 0xA8, 0x29, 0x45, 0x28, + 0xE6, 0xA0, 0xAA, 0x29, 0x45, 0x28, 0xE6, 0xB0, + 0xB4, 0x29, 0x45, 0x28, 0xE7, 0x81, 0xAB, 0x29, + // Bytes 2140 - 217f + 0x45, 0x28, 0xE7, 0x89, 0xB9, 0x29, 0x45, 0x28, + 0xE7, 0x9B, 0xA3, 0x29, 0x45, 0x28, 0xE7, 0xA4, + 0xBE, 0x29, 0x45, 0x28, 0xE7, 0xA5, 0x9D, 0x29, + 0x45, 0x28, 0xE7, 0xA5, 0xAD, 0x29, 0x45, 0x28, + 0xE8, 0x87, 0xAA, 0x29, 0x45, 0x28, 0xE8, 0x87, + 0xB3, 0x29, 0x45, 0x28, 0xE8, 0xB2, 0xA1, 0x29, + 0x45, 0x28, 0xE8, 0xB3, 0x87, 0x29, 0x45, 0x28, + 0xE9, 0x87, 0x91, 0x29, 0x45, 0x30, 0xE2, 0x81, + // Bytes 2180 - 21bf + 0x84, 0x33, 0x45, 0x31, 0x30, 0xE6, 0x97, 0xA5, + 0x45, 0x31, 0x30, 0xE6, 0x9C, 0x88, 0x45, 0x31, + 0x30, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x31, 0xE6, + 0x97, 0xA5, 0x45, 0x31, 0x31, 0xE6, 0x9C, 0x88, + 0x45, 0x31, 0x31, 0xE7, 0x82, 0xB9, 0x45, 0x31, + 0x32, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x32, 0xE6, + 0x9C, 0x88, 0x45, 0x31, 0x32, 0xE7, 0x82, 0xB9, + 0x45, 0x31, 0x33, 0xE6, 0x97, 0xA5, 0x45, 0x31, + // Bytes 21c0 - 21ff + 0x33, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x34, 0xE6, + 0x97, 0xA5, 0x45, 0x31, 0x34, 0xE7, 0x82, 0xB9, + 0x45, 0x31, 0x35, 0xE6, 0x97, 0xA5, 0x45, 0x31, + 0x35, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x36, 0xE6, + 0x97, 0xA5, 0x45, 0x31, 0x36, 0xE7, 0x82, 0xB9, + 0x45, 0x31, 0x37, 0xE6, 0x97, 0xA5, 0x45, 0x31, + 0x37, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x38, 0xE6, + 0x97, 0xA5, 0x45, 0x31, 0x38, 0xE7, 0x82, 0xB9, + // Bytes 2200 - 223f + 0x45, 0x31, 0x39, 0xE6, 0x97, 0xA5, 0x45, 0x31, + 0x39, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0xE2, 0x81, + 0x84, 0x32, 0x45, 0x31, 0xE2, 0x81, 0x84, 0x33, + 0x45, 0x31, 0xE2, 0x81, 0x84, 0x34, 0x45, 0x31, + 0xE2, 0x81, 0x84, 0x35, 0x45, 0x31, 0xE2, 0x81, + 0x84, 0x36, 0x45, 0x31, 0xE2, 0x81, 0x84, 0x37, + 0x45, 0x31, 0xE2, 0x81, 0x84, 0x38, 0x45, 0x31, + 0xE2, 0x81, 0x84, 0x39, 0x45, 0x32, 0x30, 0xE6, + // Bytes 2240 - 227f + 0x97, 0xA5, 0x45, 0x32, 0x30, 0xE7, 0x82, 0xB9, + 0x45, 0x32, 0x31, 0xE6, 0x97, 0xA5, 0x45, 0x32, + 0x31, 0xE7, 0x82, 0xB9, 0x45, 0x32, 0x32, 0xE6, + 0x97, 0xA5, 0x45, 0x32, 0x32, 0xE7, 0x82, 0xB9, + 0x45, 0x32, 0x33, 0xE6, 0x97, 0xA5, 0x45, 0x32, + 0x33, 0xE7, 0x82, 0xB9, 0x45, 0x32, 0x34, 0xE6, + 0x97, 0xA5, 0x45, 0x32, 0x34, 0xE7, 0x82, 0xB9, + 0x45, 0x32, 0x35, 0xE6, 0x97, 0xA5, 0x45, 0x32, + // Bytes 2280 - 22bf + 0x36, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0x37, 0xE6, + 0x97, 0xA5, 0x45, 0x32, 0x38, 0xE6, 0x97, 0xA5, + 0x45, 0x32, 0x39, 0xE6, 0x97, 0xA5, 0x45, 0x32, + 0xE2, 0x81, 0x84, 0x33, 0x45, 0x32, 0xE2, 0x81, + 0x84, 0x35, 0x45, 0x33, 0x30, 0xE6, 0x97, 0xA5, + 0x45, 0x33, 0x31, 0xE6, 0x97, 0xA5, 0x45, 0x33, + 0xE2, 0x81, 0x84, 0x34, 0x45, 0x33, 0xE2, 0x81, + 0x84, 0x35, 0x45, 0x33, 0xE2, 0x81, 0x84, 0x38, + // Bytes 22c0 - 22ff + 0x45, 0x34, 0xE2, 0x81, 0x84, 0x35, 0x45, 0x35, + 0xE2, 0x81, 0x84, 0x36, 0x45, 0x35, 0xE2, 0x81, + 0x84, 0x38, 0x45, 0x37, 0xE2, 0x81, 0x84, 0x38, + 0x45, 0x41, 0xE2, 0x88, 0x95, 0x6D, 0x45, 0x56, + 0xE2, 0x88, 0x95, 0x6D, 0x45, 0x6D, 0xE2, 0x88, + 0x95, 0x73, 0x46, 0x31, 0xE2, 0x81, 0x84, 0x31, + 0x30, 0x46, 0x43, 0xE2, 0x88, 0x95, 0x6B, 0x67, + 0x46, 0x6D, 0xE2, 0x88, 0x95, 0x73, 0x32, 0x46, + // Bytes 2300 - 233f + 0xD8, 0xA8, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD8, + 0xA8, 0xD8, 0xAE, 0xD9, 0x8A, 0x46, 0xD8, 0xAA, + 0xD8, 0xAC, 0xD9, 0x85, 0x46, 0xD8, 0xAA, 0xD8, + 0xAC, 0xD9, 0x89, 0x46, 0xD8, 0xAA, 0xD8, 0xAC, + 0xD9, 0x8A, 0x46, 0xD8, 0xAA, 0xD8, 0xAD, 0xD8, + 0xAC, 0x46, 0xD8, 0xAA, 0xD8, 0xAD, 0xD9, 0x85, + 0x46, 0xD8, 0xAA, 0xD8, 0xAE, 0xD9, 0x85, 0x46, + 0xD8, 0xAA, 0xD8, 0xAE, 0xD9, 0x89, 0x46, 0xD8, + // Bytes 2340 - 237f + 0xAA, 0xD8, 0xAE, 0xD9, 0x8A, 0x46, 0xD8, 0xAA, + 0xD9, 0x85, 0xD8, 0xAC, 0x46, 0xD8, 0xAA, 0xD9, + 0x85, 0xD8, 0xAD, 0x46, 0xD8, 0xAA, 0xD9, 0x85, + 0xD8, 0xAE, 0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD9, + 0x89, 0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD9, 0x8A, + 0x46, 0xD8, 0xAC, 0xD8, 0xAD, 0xD9, 0x89, 0x46, + 0xD8, 0xAC, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD8, + 0xAC, 0xD9, 0x85, 0xD8, 0xAD, 0x46, 0xD8, 0xAC, + // Bytes 2380 - 23bf + 0xD9, 0x85, 0xD9, 0x89, 0x46, 0xD8, 0xAC, 0xD9, + 0x85, 0xD9, 0x8A, 0x46, 0xD8, 0xAD, 0xD8, 0xAC, + 0xD9, 0x8A, 0x46, 0xD8, 0xAD, 0xD9, 0x85, 0xD9, + 0x89, 0x46, 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x8A, + 0x46, 0xD8, 0xB3, 0xD8, 0xAC, 0xD8, 0xAD, 0x46, + 0xD8, 0xB3, 0xD8, 0xAC, 0xD9, 0x89, 0x46, 0xD8, + 0xB3, 0xD8, 0xAD, 0xD8, 0xAC, 0x46, 0xD8, 0xB3, + 0xD8, 0xAE, 0xD9, 0x89, 0x46, 0xD8, 0xB3, 0xD8, + // Bytes 23c0 - 23ff + 0xAE, 0xD9, 0x8A, 0x46, 0xD8, 0xB3, 0xD9, 0x85, + 0xD8, 0xAC, 0x46, 0xD8, 0xB3, 0xD9, 0x85, 0xD8, + 0xAD, 0x46, 0xD8, 0xB3, 0xD9, 0x85, 0xD9, 0x85, + 0x46, 0xD8, 0xB4, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, + 0xD8, 0xB4, 0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD8, + 0xB4, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD8, 0xB4, + 0xD9, 0x85, 0xD8, 0xAE, 0x46, 0xD8, 0xB4, 0xD9, + 0x85, 0xD9, 0x85, 0x46, 0xD8, 0xB5, 0xD8, 0xAD, + // Bytes 2400 - 243f + 0xD8, 0xAD, 0x46, 0xD8, 0xB5, 0xD8, 0xAD, 0xD9, + 0x8A, 0x46, 0xD8, 0xB5, 0xD9, 0x84, 0xD9, 0x89, + 0x46, 0xD8, 0xB5, 0xD9, 0x84, 0xDB, 0x92, 0x46, + 0xD8, 0xB5, 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8, + 0xB6, 0xD8, 0xAD, 0xD9, 0x89, 0x46, 0xD8, 0xB6, + 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD8, 0xB6, 0xD8, + 0xAE, 0xD9, 0x85, 0x46, 0xD8, 0xB7, 0xD9, 0x85, + 0xD8, 0xAD, 0x46, 0xD8, 0xB7, 0xD9, 0x85, 0xD9, + // Bytes 2440 - 247f + 0x85, 0x46, 0xD8, 0xB7, 0xD9, 0x85, 0xD9, 0x8A, + 0x46, 0xD8, 0xB9, 0xD8, 0xAC, 0xD9, 0x85, 0x46, + 0xD8, 0xB9, 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8, + 0xB9, 0xD9, 0x85, 0xD9, 0x89, 0x46, 0xD8, 0xB9, + 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8, 0xBA, 0xD9, + 0x85, 0xD9, 0x85, 0x46, 0xD8, 0xBA, 0xD9, 0x85, + 0xD9, 0x89, 0x46, 0xD8, 0xBA, 0xD9, 0x85, 0xD9, + 0x8A, 0x46, 0xD9, 0x81, 0xD8, 0xAE, 0xD9, 0x85, + // Bytes 2480 - 24bf + 0x46, 0xD9, 0x81, 0xD9, 0x85, 0xD9, 0x8A, 0x46, + 0xD9, 0x82, 0xD9, 0x84, 0xDB, 0x92, 0x46, 0xD9, + 0x82, 0xD9, 0x85, 0xD8, 0xAD, 0x46, 0xD9, 0x82, + 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD9, 0x82, 0xD9, + 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x83, 0xD9, 0x85, + 0xD9, 0x85, 0x46, 0xD9, 0x83, 0xD9, 0x85, 0xD9, + 0x8A, 0x46, 0xD9, 0x84, 0xD8, 0xAC, 0xD8, 0xAC, + 0x46, 0xD9, 0x84, 0xD8, 0xAC, 0xD9, 0x85, 0x46, + // Bytes 24c0 - 24ff + 0xD9, 0x84, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD9, + 0x84, 0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD9, 0x84, + 0xD8, 0xAD, 0xD9, 0x89, 0x46, 0xD9, 0x84, 0xD8, + 0xAD, 0xD9, 0x8A, 0x46, 0xD9, 0x84, 0xD8, 0xAE, + 0xD9, 0x85, 0x46, 0xD9, 0x84, 0xD9, 0x85, 0xD8, + 0xAD, 0x46, 0xD9, 0x84, 0xD9, 0x85, 0xD9, 0x8A, + 0x46, 0xD9, 0x85, 0xD8, 0xAC, 0xD8, 0xAD, 0x46, + 0xD9, 0x85, 0xD8, 0xAC, 0xD8, 0xAE, 0x46, 0xD9, + // Bytes 2500 - 253f + 0x85, 0xD8, 0xAC, 0xD9, 0x85, 0x46, 0xD9, 0x85, + 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD9, 0x85, 0xD8, + 0xAD, 0xD8, 0xAC, 0x46, 0xD9, 0x85, 0xD8, 0xAD, + 0xD9, 0x85, 0x46, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, + 0x8A, 0x46, 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xAC, + 0x46, 0xD9, 0x85, 0xD8, 0xAE, 0xD9, 0x85, 0x46, + 0xD9, 0x85, 0xD8, 0xAE, 0xD9, 0x8A, 0x46, 0xD9, + 0x85, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x86, + // Bytes 2540 - 257f + 0xD8, 0xAC, 0xD8, 0xAD, 0x46, 0xD9, 0x86, 0xD8, + 0xAC, 0xD9, 0x85, 0x46, 0xD9, 0x86, 0xD8, 0xAC, + 0xD9, 0x89, 0x46, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, + 0x8A, 0x46, 0xD9, 0x86, 0xD8, 0xAD, 0xD9, 0x85, + 0x46, 0xD9, 0x86, 0xD8, 0xAD, 0xD9, 0x89, 0x46, + 0xD9, 0x86, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD9, + 0x86, 0xD9, 0x85, 0xD9, 0x89, 0x46, 0xD9, 0x86, + 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x87, 0xD9, + // Bytes 2580 - 25bf + 0x85, 0xD8, 0xAC, 0x46, 0xD9, 0x87, 0xD9, 0x85, + 0xD9, 0x85, 0x46, 0xD9, 0x8A, 0xD8, 0xAC, 0xD9, + 0x8A, 0x46, 0xD9, 0x8A, 0xD8, 0xAD, 0xD9, 0x8A, + 0x46, 0xD9, 0x8A, 0xD9, 0x85, 0xD9, 0x85, 0x46, + 0xD9, 0x8A, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, + 0x8A, 0xD9, 0x94, 0xD8, 0xA7, 0x46, 0xD9, 0x8A, + 0xD9, 0x94, 0xD8, 0xAC, 0x46, 0xD9, 0x8A, 0xD9, + 0x94, 0xD8, 0xAD, 0x46, 0xD9, 0x8A, 0xD9, 0x94, + // Bytes 25c0 - 25ff + 0xD8, 0xAE, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, + 0xB1, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xB2, + 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x85, 0x46, + 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x86, 0x46, 0xD9, + 0x8A, 0xD9, 0x94, 0xD9, 0x87, 0x46, 0xD9, 0x8A, + 0xD9, 0x94, 0xD9, 0x88, 0x46, 0xD9, 0x8A, 0xD9, + 0x94, 0xD9, 0x89, 0x46, 0xD9, 0x8A, 0xD9, 0x94, + 0xD9, 0x8A, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, + // Bytes 2600 - 263f + 0x86, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x87, + 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x88, 0x46, + 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x90, 0x46, 0xD9, + 0x8A, 0xD9, 0x94, 0xDB, 0x95, 0x46, 0xE0, 0xB9, + 0x8D, 0xE0, 0xB8, 0xB2, 0x46, 0xE0, 0xBA, 0xAB, + 0xE0, 0xBA, 0x99, 0x46, 0xE0, 0xBA, 0xAB, 0xE0, + 0xBA, 0xA1, 0x46, 0xE0, 0xBB, 0x8D, 0xE0, 0xBA, + 0xB2, 0x46, 0xE0, 0xBD, 0x80, 0xE0, 0xBE, 0xB5, + // Bytes 2640 - 267f + 0x46, 0xE0, 0xBD, 0x82, 0xE0, 0xBE, 0xB7, 0x46, + 0xE0, 0xBD, 0x8C, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, + 0xBD, 0x91, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBD, + 0x96, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBD, 0x9B, + 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBE, 0x90, 0xE0, + 0xBE, 0xB5, 0x46, 0xE0, 0xBE, 0x92, 0xE0, 0xBE, + 0xB7, 0x46, 0xE0, 0xBE, 0x9C, 0xE0, 0xBE, 0xB7, + 0x46, 0xE0, 0xBE, 0xA1, 0xE0, 0xBE, 0xB7, 0x46, + // Bytes 2680 - 26bf + 0xE0, 0xBE, 0xA6, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, + 0xBE, 0xAB, 0xE0, 0xBE, 0xB7, 0x46, 0xE2, 0x80, + 0xB2, 0xE2, 0x80, 0xB2, 0x46, 0xE2, 0x80, 0xB5, + 0xE2, 0x80, 0xB5, 0x46, 0xE2, 0x88, 0xAB, 0xE2, + 0x88, 0xAB, 0x46, 0xE2, 0x88, 0xAE, 0xE2, 0x88, + 0xAE, 0x46, 0xE3, 0x81, 0xBB, 0xE3, 0x81, 0x8B, + 0x46, 0xE3, 0x82, 0x88, 0xE3, 0x82, 0x8A, 0x46, + 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xAD, 0x46, 0xE3, + // Bytes 26c0 - 26ff + 0x82, 0xB3, 0xE3, 0x82, 0xB3, 0x46, 0xE3, 0x82, + 0xB3, 0xE3, 0x83, 0x88, 0x46, 0xE3, 0x83, 0x88, + 0xE3, 0x83, 0xB3, 0x46, 0xE3, 0x83, 0x8A, 0xE3, + 0x83, 0x8E, 0x46, 0xE3, 0x83, 0x9B, 0xE3, 0x83, + 0xB3, 0x46, 0xE3, 0x83, 0x9F, 0xE3, 0x83, 0xAA, + 0x46, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xA9, 0x46, + 0xE3, 0x83, 0xAC, 0xE3, 0x83, 0xA0, 0x46, 0xE5, + 0xA4, 0xA7, 0xE6, 0xAD, 0xA3, 0x46, 0xE5, 0xB9, + // Bytes 2700 - 273f + 0xB3, 0xE6, 0x88, 0x90, 0x46, 0xE6, 0x98, 0x8E, + 0xE6, 0xB2, 0xBB, 0x46, 0xE6, 0x98, 0xAD, 0xE5, + 0x92, 0x8C, 0x47, 0x72, 0x61, 0x64, 0xE2, 0x88, + 0x95, 0x73, 0x47, 0xE3, 0x80, 0x94, 0x53, 0xE3, + 0x80, 0x95, 0x48, 0x28, 0xE1, 0x84, 0x80, 0xE1, + 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x82, + 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, + 0x83, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, + // Bytes 2740 - 277f + 0x84, 0x85, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, + 0xE1, 0x84, 0x86, 0xE1, 0x85, 0xA1, 0x29, 0x48, + 0x28, 0xE1, 0x84, 0x87, 0xE1, 0x85, 0xA1, 0x29, + 0x48, 0x28, 0xE1, 0x84, 0x89, 0xE1, 0x85, 0xA1, + 0x29, 0x48, 0x28, 0xE1, 0x84, 0x8B, 0xE1, 0x85, + 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x8C, 0xE1, + 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x8C, + 0xE1, 0x85, 0xAE, 0x29, 0x48, 0x28, 0xE1, 0x84, + // Bytes 2780 - 27bf + 0x8E, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, + 0x84, 0x8F, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, + 0xE1, 0x84, 0x90, 0xE1, 0x85, 0xA1, 0x29, 0x48, + 0x28, 0xE1, 0x84, 0x91, 0xE1, 0x85, 0xA1, 0x29, + 0x48, 0x28, 0xE1, 0x84, 0x92, 0xE1, 0x85, 0xA1, + 0x29, 0x48, 0x72, 0x61, 0x64, 0xE2, 0x88, 0x95, + 0x73, 0x32, 0x48, 0xD8, 0xA7, 0xD9, 0x83, 0xD8, + 0xA8, 0xD8, 0xB1, 0x48, 0xD8, 0xA7, 0xD9, 0x84, + // Bytes 27c0 - 27ff + 0xD9, 0x84, 0xD9, 0x87, 0x48, 0xD8, 0xB1, 0xD8, + 0xB3, 0xD9, 0x88, 0xD9, 0x84, 0x48, 0xD8, 0xB1, + 0xDB, 0x8C, 0xD8, 0xA7, 0xD9, 0x84, 0x48, 0xD8, + 0xB5, 0xD9, 0x84, 0xD8, 0xB9, 0xD9, 0x85, 0x48, + 0xD8, 0xB9, 0xD9, 0x84, 0xD9, 0x8A, 0xD9, 0x87, + 0x48, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, + 0xAF, 0x48, 0xD9, 0x88, 0xD8, 0xB3, 0xD9, 0x84, + 0xD9, 0x85, 0x49, 0xE2, 0x80, 0xB2, 0xE2, 0x80, + // Bytes 2800 - 283f + 0xB2, 0xE2, 0x80, 0xB2, 0x49, 0xE2, 0x80, 0xB5, + 0xE2, 0x80, 0xB5, 0xE2, 0x80, 0xB5, 0x49, 0xE2, + 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, + 0x49, 0xE2, 0x88, 0xAE, 0xE2, 0x88, 0xAE, 0xE2, + 0x88, 0xAE, 0x49, 0xE3, 0x80, 0x94, 0xE4, 0xB8, + 0x89, 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, + 0xE4, 0xBA, 0x8C, 0xE3, 0x80, 0x95, 0x49, 0xE3, + 0x80, 0x94, 0xE5, 0x8B, 0x9D, 0xE3, 0x80, 0x95, + // Bytes 2840 - 287f + 0x49, 0xE3, 0x80, 0x94, 0xE5, 0xAE, 0x89, 0xE3, + 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE6, 0x89, + 0x93, 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, + 0xE6, 0x95, 0x97, 0xE3, 0x80, 0x95, 0x49, 0xE3, + 0x80, 0x94, 0xE6, 0x9C, 0xAC, 0xE3, 0x80, 0x95, + 0x49, 0xE3, 0x80, 0x94, 0xE7, 0x82, 0xB9, 0xE3, + 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE7, 0x9B, + 0x97, 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x82, 0xA2, + // Bytes 2880 - 28bf + 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0x49, 0xE3, + 0x82, 0xA4, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x81, + 0x49, 0xE3, 0x82, 0xA6, 0xE3, 0x82, 0xA9, 0xE3, + 0x83, 0xB3, 0x49, 0xE3, 0x82, 0xAA, 0xE3, 0x83, + 0xB3, 0xE3, 0x82, 0xB9, 0x49, 0xE3, 0x82, 0xAA, + 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xA0, 0x49, 0xE3, + 0x82, 0xAB, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xAA, + 0x49, 0xE3, 0x82, 0xB1, 0xE3, 0x83, 0xBC, 0xE3, + // Bytes 28c0 - 28ff + 0x82, 0xB9, 0x49, 0xE3, 0x82, 0xB3, 0xE3, 0x83, + 0xAB, 0xE3, 0x83, 0x8A, 0x49, 0xE3, 0x82, 0xBB, + 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x81, 0x49, 0xE3, + 0x82, 0xBB, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, + 0x49, 0xE3, 0x83, 0x86, 0xE3, 0x82, 0x99, 0xE3, + 0x82, 0xB7, 0x49, 0xE3, 0x83, 0x88, 0xE3, 0x82, + 0x99, 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x83, 0x8E, + 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, 0x49, 0xE3, + // Bytes 2900 - 293f + 0x83, 0x8F, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0x84, + 0x49, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x99, 0xE3, + 0x83, 0xAB, 0x49, 0xE3, 0x83, 0x92, 0xE3, 0x82, + 0x9A, 0xE3, 0x82, 0xB3, 0x49, 0xE3, 0x83, 0x95, + 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0xB3, 0x49, 0xE3, + 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xBD, + 0x49, 0xE3, 0x83, 0x98, 0xE3, 0x83, 0xAB, 0xE3, + 0x83, 0x84, 0x49, 0xE3, 0x83, 0x9B, 0xE3, 0x83, + // Bytes 2940 - 297f + 0xBC, 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x83, 0x9B, + 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xB3, 0x49, 0xE3, + 0x83, 0x9E, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xAB, + 0x49, 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0x83, 0xE3, + 0x83, 0x8F, 0x49, 0xE3, 0x83, 0x9E, 0xE3, 0x83, + 0xAB, 0xE3, 0x82, 0xAF, 0x49, 0xE3, 0x83, 0xA4, + 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0x49, 0xE3, + 0x83, 0xA6, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xB3, + // Bytes 2980 - 29bf + 0x49, 0xE3, 0x83, 0xAF, 0xE3, 0x83, 0x83, 0xE3, + 0x83, 0x88, 0x4C, 0xE2, 0x80, 0xB2, 0xE2, 0x80, + 0xB2, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2, 0x4C, + 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, + 0xAB, 0xE2, 0x88, 0xAB, 0x4C, 0xE3, 0x82, 0xA2, + 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x95, 0xE3, 0x82, + 0xA1, 0x4C, 0xE3, 0x82, 0xA8, 0xE3, 0x83, 0xBC, + 0xE3, 0x82, 0xAB, 0xE3, 0x83, 0xBC, 0x4C, 0xE3, + // Bytes 29c0 - 29ff + 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAD, + 0xE3, 0x83, 0xB3, 0x4C, 0xE3, 0x82, 0xAB, 0xE3, + 0x82, 0x99, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x9E, + 0x4C, 0xE3, 0x82, 0xAB, 0xE3, 0x83, 0xA9, 0xE3, + 0x83, 0x83, 0xE3, 0x83, 0x88, 0x4C, 0xE3, 0x82, + 0xAB, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xAA, 0xE3, + 0x83, 0xBC, 0x4C, 0xE3, 0x82, 0xAD, 0xE3, 0x82, + 0x99, 0xE3, 0x83, 0x8B, 0xE3, 0x83, 0xBC, 0x4C, + // Bytes 2a00 - 2a3f + 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xA5, 0xE3, 0x83, + 0xAA, 0xE3, 0x83, 0xBC, 0x4C, 0xE3, 0x82, 0xAF, + 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xA9, 0xE3, 0x83, + 0xA0, 0x4C, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAD, + 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x8D, 0x4C, 0xE3, + 0x82, 0xB5, 0xE3, 0x82, 0xA4, 0xE3, 0x82, 0xAF, + 0xE3, 0x83, 0xAB, 0x4C, 0xE3, 0x82, 0xBF, 0xE3, + 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xB9, + // Bytes 2a40 - 2a7f + 0x4C, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x9A, 0xE3, + 0x83, 0xBC, 0xE3, 0x83, 0x84, 0x4C, 0xE3, 0x83, + 0x92, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xAF, 0xE3, + 0x83, 0xAB, 0x4C, 0xE3, 0x83, 0x95, 0xE3, 0x82, + 0xA3, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88, 0x4C, + 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x99, 0xE3, 0x83, + 0xBC, 0xE3, 0x82, 0xBF, 0x4C, 0xE3, 0x83, 0x98, + 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0x8B, 0xE3, 0x83, + // Bytes 2a80 - 2abf + 0x92, 0x4C, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x9A, + 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xB9, 0x4C, 0xE3, + 0x83, 0x9B, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB, + 0xE3, 0x83, 0x88, 0x4C, 0xE3, 0x83, 0x9E, 0xE3, + 0x82, 0xA4, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAD, + 0x4C, 0xE3, 0x83, 0x9F, 0xE3, 0x82, 0xAF, 0xE3, + 0x83, 0xAD, 0xE3, 0x83, 0xB3, 0x4C, 0xE3, 0x83, + 0xA1, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88, 0xE3, + // Bytes 2ac0 - 2aff + 0x83, 0xAB, 0x4C, 0xE3, 0x83, 0xAA, 0xE3, 0x83, + 0x83, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0x4C, + 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x92, 0xE3, 0x82, + 0x9A, 0xE3, 0x83, 0xBC, 0x4C, 0xE6, 0xA0, 0xAA, + 0xE5, 0xBC, 0x8F, 0xE4, 0xBC, 0x9A, 0xE7, 0xA4, + 0xBE, 0x4E, 0x28, 0xE1, 0x84, 0x8B, 0xE1, 0x85, + 0xA9, 0xE1, 0x84, 0x92, 0xE1, 0x85, 0xAE, 0x29, + 0x4F, 0xD8, 0xAC, 0xD9, 0x84, 0x20, 0xD8, 0xAC, + // Bytes 2b00 - 2b3f + 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x84, 0xD9, 0x87, + 0x4F, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0x8F, 0xE3, + 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88, + 0x4F, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xB3, 0xE3, + 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xA2, + 0x4F, 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xAD, 0xE3, + 0x83, 0xAF, 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, + 0x4F, 0xE3, 0x82, 0xB5, 0xE3, 0x83, 0xB3, 0xE3, + // Bytes 2b40 - 2b7f + 0x83, 0x81, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xA0, + 0x4F, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x99, 0xE3, + 0x83, 0xBC, 0xE3, 0x83, 0xAC, 0xE3, 0x83, 0xAB, + 0x4F, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0xAF, 0xE3, + 0x82, 0xBF, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xAB, + 0x4F, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A, 0xE3, + 0x82, 0xA4, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, + 0x4F, 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0xB3, 0xE3, + // Bytes 2b80 - 2bbf + 0x82, 0xB7, 0xE3, 0x83, 0xA7, 0xE3, 0x83, 0xB3, + 0x4F, 0xE3, 0x83, 0xA1, 0xE3, 0x82, 0xAB, 0xE3, + 0x82, 0x99, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xB3, + 0x4F, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0xBC, 0xE3, + 0x83, 0x95, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB, + 0x51, 0x28, 0xE1, 0x84, 0x8B, 0xE1, 0x85, 0xA9, + 0xE1, 0x84, 0x8C, 0xE1, 0x85, 0xA5, 0xE1, 0x86, + 0xAB, 0x29, 0x52, 0xE3, 0x82, 0xAD, 0xE3, 0x82, + // Bytes 2bc0 - 2bff + 0x99, 0xE3, 0x83, 0xAB, 0xE3, 0x82, 0xBF, 0xE3, + 0x82, 0x99, 0xE3, 0x83, 0xBC, 0x52, 0xE3, 0x82, + 0xAD, 0xE3, 0x83, 0xAD, 0xE3, 0x82, 0xAF, 0xE3, + 0x82, 0x99, 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0xA0, + 0x52, 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xAD, 0xE3, + 0x83, 0xA1, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88, + 0xE3, 0x83, 0xAB, 0x52, 0xE3, 0x82, 0xAF, 0xE3, + 0x82, 0x99, 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0xA0, + // Bytes 2c00 - 2c3f + 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xB3, 0x52, 0xE3, + 0x82, 0xAF, 0xE3, 0x83, 0xAB, 0xE3, 0x82, 0xBB, + 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xA4, 0xE3, 0x83, + 0xAD, 0x52, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x9A, + 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xBB, 0xE3, 0x83, + 0xB3, 0xE3, 0x83, 0x88, 0x52, 0xE3, 0x83, 0x92, + 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xA2, 0xE3, 0x82, + 0xB9, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0x52, + // Bytes 2c40 - 2c7f + 0xE3, 0x83, 0x95, 0xE3, 0x82, 0x99, 0xE3, 0x83, + 0x83, 0xE3, 0x82, 0xB7, 0xE3, 0x82, 0xA7, 0xE3, + 0x83, 0xAB, 0x52, 0xE3, 0x83, 0x9F, 0xE3, 0x83, + 0xAA, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x99, 0xE3, + 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0x52, 0xE3, 0x83, + 0xAC, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, 0xE3, + 0x82, 0xB1, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xB3, + 0x61, 0xD8, 0xB5, 0xD9, 0x84, 0xD9, 0x89, 0x20, + // Bytes 2c80 - 2cbf + 0xD8, 0xA7, 0xD9, 0x84, 0xD9, 0x84, 0xD9, 0x87, + 0x20, 0xD8, 0xB9, 0xD9, 0x84, 0xD9, 0x8A, 0xD9, + 0x87, 0x20, 0xD9, 0x88, 0xD8, 0xB3, 0xD9, 0x84, + 0xD9, 0x85, 0x06, 0xE0, 0xA7, 0x87, 0xE0, 0xA6, + 0xBE, 0x01, 0x06, 0xE0, 0xA7, 0x87, 0xE0, 0xA7, + 0x97, 0x01, 0x06, 0xE0, 0xAD, 0x87, 0xE0, 0xAC, + 0xBE, 0x01, 0x06, 0xE0, 0xAD, 0x87, 0xE0, 0xAD, + 0x96, 0x01, 0x06, 0xE0, 0xAD, 0x87, 0xE0, 0xAD, + // Bytes 2cc0 - 2cff + 0x97, 0x01, 0x06, 0xE0, 0xAE, 0x92, 0xE0, 0xAF, + 0x97, 0x01, 0x06, 0xE0, 0xAF, 0x86, 0xE0, 0xAE, + 0xBE, 0x01, 0x06, 0xE0, 0xAF, 0x86, 0xE0, 0xAF, + 0x97, 0x01, 0x06, 0xE0, 0xAF, 0x87, 0xE0, 0xAE, + 0xBE, 0x01, 0x06, 0xE0, 0xB2, 0xBF, 0xE0, 0xB3, + 0x95, 0x01, 0x06, 0xE0, 0xB3, 0x86, 0xE0, 0xB3, + 0x95, 0x01, 0x06, 0xE0, 0xB3, 0x86, 0xE0, 0xB3, + 0x96, 0x01, 0x06, 0xE0, 0xB5, 0x86, 0xE0, 0xB4, + // Bytes 2d00 - 2d3f + 0xBE, 0x01, 0x06, 0xE0, 0xB5, 0x86, 0xE0, 0xB5, + 0x97, 0x01, 0x06, 0xE0, 0xB5, 0x87, 0xE0, 0xB4, + 0xBE, 0x01, 0x06, 0xE0, 0xB7, 0x99, 0xE0, 0xB7, + 0x9F, 0x01, 0x06, 0xE1, 0x80, 0xA5, 0xE1, 0x80, + 0xAE, 0x01, 0x06, 0xE1, 0xAC, 0x85, 0xE1, 0xAC, + 0xB5, 0x01, 0x06, 0xE1, 0xAC, 0x87, 0xE1, 0xAC, + 0xB5, 0x01, 0x06, 0xE1, 0xAC, 0x89, 0xE1, 0xAC, + 0xB5, 0x01, 0x06, 0xE1, 0xAC, 0x8B, 0xE1, 0xAC, + // Bytes 2d40 - 2d7f + 0xB5, 0x01, 0x06, 0xE1, 0xAC, 0x8D, 0xE1, 0xAC, + 0xB5, 0x01, 0x06, 0xE1, 0xAC, 0x91, 0xE1, 0xAC, + 0xB5, 0x01, 0x06, 0xE1, 0xAC, 0xBA, 0xE1, 0xAC, + 0xB5, 0x01, 0x06, 0xE1, 0xAC, 0xBC, 0xE1, 0xAC, + 0xB5, 0x01, 0x06, 0xE1, 0xAC, 0xBE, 0xE1, 0xAC, + 0xB5, 0x01, 0x06, 0xE1, 0xAC, 0xBF, 0xE1, 0xAC, + 0xB5, 0x01, 0x06, 0xE1, 0xAD, 0x82, 0xE1, 0xAC, + 0xB5, 0x01, 0x08, 0xF0, 0x91, 0x84, 0xB1, 0xF0, + // Bytes 2d80 - 2dbf + 0x91, 0x84, 0xA7, 0x01, 0x08, 0xF0, 0x91, 0x84, + 0xB2, 0xF0, 0x91, 0x84, 0xA7, 0x01, 0x08, 0xF0, + 0x91, 0x8D, 0x87, 0xF0, 0x91, 0x8C, 0xBE, 0x01, + 0x08, 0xF0, 0x91, 0x8D, 0x87, 0xF0, 0x91, 0x8D, + 0x97, 0x01, 0x08, 0xF0, 0x91, 0x92, 0xB9, 0xF0, + 0x91, 0x92, 0xB0, 0x01, 0x08, 0xF0, 0x91, 0x92, + 0xB9, 0xF0, 0x91, 0x92, 0xBA, 0x01, 0x08, 0xF0, + 0x91, 0x92, 0xB9, 0xF0, 0x91, 0x92, 0xBD, 0x01, + // Bytes 2dc0 - 2dff + 0x08, 0xF0, 0x91, 0x96, 0xB8, 0xF0, 0x91, 0x96, + 0xAF, 0x01, 0x08, 0xF0, 0x91, 0x96, 0xB9, 0xF0, + 0x91, 0x96, 0xAF, 0x01, 0x09, 0xE0, 0xB3, 0x86, + 0xE0, 0xB3, 0x82, 0xE0, 0xB3, 0x95, 0x02, 0x09, + 0xE0, 0xB7, 0x99, 0xE0, 0xB7, 0x8F, 0xE0, 0xB7, + 0x8A, 0x12, 0x44, 0x44, 0x5A, 0xCC, 0x8C, 0xC9, + 0x44, 0x44, 0x7A, 0xCC, 0x8C, 0xC9, 0x44, 0x64, + 0x7A, 0xCC, 0x8C, 0xC9, 0x46, 0xD9, 0x84, 0xD8, + // Bytes 2e00 - 2e3f + 0xA7, 0xD9, 0x93, 0xC9, 0x46, 0xD9, 0x84, 0xD8, + 0xA7, 0xD9, 0x94, 0xC9, 0x46, 0xD9, 0x84, 0xD8, + 0xA7, 0xD9, 0x95, 0xB5, 0x46, 0xE1, 0x84, 0x80, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x82, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x83, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x85, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x86, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x87, + // Bytes 2e40 - 2e7f + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x89, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x8B, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x8B, + 0xE1, 0x85, 0xAE, 0x01, 0x46, 0xE1, 0x84, 0x8C, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x8E, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x8F, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x90, + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x91, + // Bytes 2e80 - 2ebf + 0xE1, 0x85, 0xA1, 0x01, 0x46, 0xE1, 0x84, 0x92, + 0xE1, 0x85, 0xA1, 0x01, 0x49, 0xE3, 0x83, 0xA1, + 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0x0D, 0x4C, + 0xE1, 0x84, 0x8C, 0xE1, 0x85, 0xAE, 0xE1, 0x84, + 0x8B, 0xE1, 0x85, 0xB4, 0x01, 0x4C, 0xE3, 0x82, + 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xAB, 0xE3, + 0x82, 0x99, 0x0D, 0x4C, 0xE3, 0x82, 0xB3, 0xE3, + 0x83, 0xBC, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A, + // Bytes 2ec0 - 2eff + 0x0D, 0x4C, 0xE3, 0x83, 0xA4, 0xE3, 0x83, 0xBC, + 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, 0x0D, 0x4F, + 0xE1, 0x84, 0x8E, 0xE1, 0x85, 0xA1, 0xE1, 0x86, + 0xB7, 0xE1, 0x84, 0x80, 0xE1, 0x85, 0xA9, 0x01, + 0x4F, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0x8B, 0xE3, + 0x83, 0xB3, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, + 0x0D, 0x4F, 0xE3, 0x82, 0xB7, 0xE3, 0x83, 0xAA, + 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xAF, 0xE3, 0x82, + // Bytes 2f00 - 2f3f + 0x99, 0x0D, 0x4F, 0xE3, 0x83, 0x98, 0xE3, 0x82, + 0x9A, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xB7, 0xE3, + 0x82, 0x99, 0x0D, 0x4F, 0xE3, 0x83, 0x9B, 0xE3, + 0x82, 0x9A, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, + 0xE3, 0x82, 0x99, 0x0D, 0x52, 0xE3, 0x82, 0xA8, + 0xE3, 0x82, 0xB9, 0xE3, 0x82, 0xAF, 0xE3, 0x83, + 0xBC, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, 0x0D, + 0x52, 0xE3, 0x83, 0x95, 0xE3, 0x82, 0xA1, 0xE3, + // Bytes 2f40 - 2f7f + 0x83, 0xA9, 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, + 0xE3, 0x82, 0x99, 0x0D, 0x86, 0xE0, 0xB3, 0x86, + 0xE0, 0xB3, 0x82, 0x01, 0x86, 0xE0, 0xB7, 0x99, + 0xE0, 0xB7, 0x8F, 0x01, 0x03, 0x3C, 0xCC, 0xB8, + 0x05, 0x03, 0x3D, 0xCC, 0xB8, 0x05, 0x03, 0x3E, + 0xCC, 0xB8, 0x05, 0x03, 0x41, 0xCC, 0x80, 0xC9, + 0x03, 0x41, 0xCC, 0x81, 0xC9, 0x03, 0x41, 0xCC, + 0x83, 0xC9, 0x03, 0x41, 0xCC, 0x84, 0xC9, 0x03, + // Bytes 2f80 - 2fbf + 0x41, 0xCC, 0x89, 0xC9, 0x03, 0x41, 0xCC, 0x8C, + 0xC9, 0x03, 0x41, 0xCC, 0x8F, 0xC9, 0x03, 0x41, + 0xCC, 0x91, 0xC9, 0x03, 0x41, 0xCC, 0xA5, 0xB5, + 0x03, 0x41, 0xCC, 0xA8, 0xA5, 0x03, 0x42, 0xCC, + 0x87, 0xC9, 0x03, 0x42, 0xCC, 0xA3, 0xB5, 0x03, + 0x42, 0xCC, 0xB1, 0xB5, 0x03, 0x43, 0xCC, 0x81, + 0xC9, 0x03, 0x43, 0xCC, 0x82, 0xC9, 0x03, 0x43, + 0xCC, 0x87, 0xC9, 0x03, 0x43, 0xCC, 0x8C, 0xC9, + // Bytes 2fc0 - 2fff + 0x03, 0x44, 0xCC, 0x87, 0xC9, 0x03, 0x44, 0xCC, + 0x8C, 0xC9, 0x03, 0x44, 0xCC, 0xA3, 0xB5, 0x03, + 0x44, 0xCC, 0xA7, 0xA5, 0x03, 0x44, 0xCC, 0xAD, + 0xB5, 0x03, 0x44, 0xCC, 0xB1, 0xB5, 0x03, 0x45, + 0xCC, 0x80, 0xC9, 0x03, 0x45, 0xCC, 0x81, 0xC9, + 0x03, 0x45, 0xCC, 0x83, 0xC9, 0x03, 0x45, 0xCC, + 0x86, 0xC9, 0x03, 0x45, 0xCC, 0x87, 0xC9, 0x03, + 0x45, 0xCC, 0x88, 0xC9, 0x03, 0x45, 0xCC, 0x89, + // Bytes 3000 - 303f + 0xC9, 0x03, 0x45, 0xCC, 0x8C, 0xC9, 0x03, 0x45, + 0xCC, 0x8F, 0xC9, 0x03, 0x45, 0xCC, 0x91, 0xC9, + 0x03, 0x45, 0xCC, 0xA8, 0xA5, 0x03, 0x45, 0xCC, + 0xAD, 0xB5, 0x03, 0x45, 0xCC, 0xB0, 0xB5, 0x03, + 0x46, 0xCC, 0x87, 0xC9, 0x03, 0x47, 0xCC, 0x81, + 0xC9, 0x03, 0x47, 0xCC, 0x82, 0xC9, 0x03, 0x47, + 0xCC, 0x84, 0xC9, 0x03, 0x47, 0xCC, 0x86, 0xC9, + 0x03, 0x47, 0xCC, 0x87, 0xC9, 0x03, 0x47, 0xCC, + // Bytes 3040 - 307f + 0x8C, 0xC9, 0x03, 0x47, 0xCC, 0xA7, 0xA5, 0x03, + 0x48, 0xCC, 0x82, 0xC9, 0x03, 0x48, 0xCC, 0x87, + 0xC9, 0x03, 0x48, 0xCC, 0x88, 0xC9, 0x03, 0x48, + 0xCC, 0x8C, 0xC9, 0x03, 0x48, 0xCC, 0xA3, 0xB5, + 0x03, 0x48, 0xCC, 0xA7, 0xA5, 0x03, 0x48, 0xCC, + 0xAE, 0xB5, 0x03, 0x49, 0xCC, 0x80, 0xC9, 0x03, + 0x49, 0xCC, 0x81, 0xC9, 0x03, 0x49, 0xCC, 0x82, + 0xC9, 0x03, 0x49, 0xCC, 0x83, 0xC9, 0x03, 0x49, + // Bytes 3080 - 30bf + 0xCC, 0x84, 0xC9, 0x03, 0x49, 0xCC, 0x86, 0xC9, + 0x03, 0x49, 0xCC, 0x87, 0xC9, 0x03, 0x49, 0xCC, + 0x89, 0xC9, 0x03, 0x49, 0xCC, 0x8C, 0xC9, 0x03, + 0x49, 0xCC, 0x8F, 0xC9, 0x03, 0x49, 0xCC, 0x91, + 0xC9, 0x03, 0x49, 0xCC, 0xA3, 0xB5, 0x03, 0x49, + 0xCC, 0xA8, 0xA5, 0x03, 0x49, 0xCC, 0xB0, 0xB5, + 0x03, 0x4A, 0xCC, 0x82, 0xC9, 0x03, 0x4B, 0xCC, + 0x81, 0xC9, 0x03, 0x4B, 0xCC, 0x8C, 0xC9, 0x03, + // Bytes 30c0 - 30ff + 0x4B, 0xCC, 0xA3, 0xB5, 0x03, 0x4B, 0xCC, 0xA7, + 0xA5, 0x03, 0x4B, 0xCC, 0xB1, 0xB5, 0x03, 0x4C, + 0xCC, 0x81, 0xC9, 0x03, 0x4C, 0xCC, 0x8C, 0xC9, + 0x03, 0x4C, 0xCC, 0xA7, 0xA5, 0x03, 0x4C, 0xCC, + 0xAD, 0xB5, 0x03, 0x4C, 0xCC, 0xB1, 0xB5, 0x03, + 0x4D, 0xCC, 0x81, 0xC9, 0x03, 0x4D, 0xCC, 0x87, + 0xC9, 0x03, 0x4D, 0xCC, 0xA3, 0xB5, 0x03, 0x4E, + 0xCC, 0x80, 0xC9, 0x03, 0x4E, 0xCC, 0x81, 0xC9, + // Bytes 3100 - 313f + 0x03, 0x4E, 0xCC, 0x83, 0xC9, 0x03, 0x4E, 0xCC, + 0x87, 0xC9, 0x03, 0x4E, 0xCC, 0x8C, 0xC9, 0x03, + 0x4E, 0xCC, 0xA3, 0xB5, 0x03, 0x4E, 0xCC, 0xA7, + 0xA5, 0x03, 0x4E, 0xCC, 0xAD, 0xB5, 0x03, 0x4E, + 0xCC, 0xB1, 0xB5, 0x03, 0x4F, 0xCC, 0x80, 0xC9, + 0x03, 0x4F, 0xCC, 0x81, 0xC9, 0x03, 0x4F, 0xCC, + 0x86, 0xC9, 0x03, 0x4F, 0xCC, 0x89, 0xC9, 0x03, + 0x4F, 0xCC, 0x8B, 0xC9, 0x03, 0x4F, 0xCC, 0x8C, + // Bytes 3140 - 317f + 0xC9, 0x03, 0x4F, 0xCC, 0x8F, 0xC9, 0x03, 0x4F, + 0xCC, 0x91, 0xC9, 0x03, 0x50, 0xCC, 0x81, 0xC9, + 0x03, 0x50, 0xCC, 0x87, 0xC9, 0x03, 0x52, 0xCC, + 0x81, 0xC9, 0x03, 0x52, 0xCC, 0x87, 0xC9, 0x03, + 0x52, 0xCC, 0x8C, 0xC9, 0x03, 0x52, 0xCC, 0x8F, + 0xC9, 0x03, 0x52, 0xCC, 0x91, 0xC9, 0x03, 0x52, + 0xCC, 0xA7, 0xA5, 0x03, 0x52, 0xCC, 0xB1, 0xB5, + 0x03, 0x53, 0xCC, 0x82, 0xC9, 0x03, 0x53, 0xCC, + // Bytes 3180 - 31bf + 0x87, 0xC9, 0x03, 0x53, 0xCC, 0xA6, 0xB5, 0x03, + 0x53, 0xCC, 0xA7, 0xA5, 0x03, 0x54, 0xCC, 0x87, + 0xC9, 0x03, 0x54, 0xCC, 0x8C, 0xC9, 0x03, 0x54, + 0xCC, 0xA3, 0xB5, 0x03, 0x54, 0xCC, 0xA6, 0xB5, + 0x03, 0x54, 0xCC, 0xA7, 0xA5, 0x03, 0x54, 0xCC, + 0xAD, 0xB5, 0x03, 0x54, 0xCC, 0xB1, 0xB5, 0x03, + 0x55, 0xCC, 0x80, 0xC9, 0x03, 0x55, 0xCC, 0x81, + 0xC9, 0x03, 0x55, 0xCC, 0x82, 0xC9, 0x03, 0x55, + // Bytes 31c0 - 31ff + 0xCC, 0x86, 0xC9, 0x03, 0x55, 0xCC, 0x89, 0xC9, + 0x03, 0x55, 0xCC, 0x8A, 0xC9, 0x03, 0x55, 0xCC, + 0x8B, 0xC9, 0x03, 0x55, 0xCC, 0x8C, 0xC9, 0x03, + 0x55, 0xCC, 0x8F, 0xC9, 0x03, 0x55, 0xCC, 0x91, + 0xC9, 0x03, 0x55, 0xCC, 0xA3, 0xB5, 0x03, 0x55, + 0xCC, 0xA4, 0xB5, 0x03, 0x55, 0xCC, 0xA8, 0xA5, + 0x03, 0x55, 0xCC, 0xAD, 0xB5, 0x03, 0x55, 0xCC, + 0xB0, 0xB5, 0x03, 0x56, 0xCC, 0x83, 0xC9, 0x03, + // Bytes 3200 - 323f + 0x56, 0xCC, 0xA3, 0xB5, 0x03, 0x57, 0xCC, 0x80, + 0xC9, 0x03, 0x57, 0xCC, 0x81, 0xC9, 0x03, 0x57, + 0xCC, 0x82, 0xC9, 0x03, 0x57, 0xCC, 0x87, 0xC9, + 0x03, 0x57, 0xCC, 0x88, 0xC9, 0x03, 0x57, 0xCC, + 0xA3, 0xB5, 0x03, 0x58, 0xCC, 0x87, 0xC9, 0x03, + 0x58, 0xCC, 0x88, 0xC9, 0x03, 0x59, 0xCC, 0x80, + 0xC9, 0x03, 0x59, 0xCC, 0x81, 0xC9, 0x03, 0x59, + 0xCC, 0x82, 0xC9, 0x03, 0x59, 0xCC, 0x83, 0xC9, + // Bytes 3240 - 327f + 0x03, 0x59, 0xCC, 0x84, 0xC9, 0x03, 0x59, 0xCC, + 0x87, 0xC9, 0x03, 0x59, 0xCC, 0x88, 0xC9, 0x03, + 0x59, 0xCC, 0x89, 0xC9, 0x03, 0x59, 0xCC, 0xA3, + 0xB5, 0x03, 0x5A, 0xCC, 0x81, 0xC9, 0x03, 0x5A, + 0xCC, 0x82, 0xC9, 0x03, 0x5A, 0xCC, 0x87, 0xC9, + 0x03, 0x5A, 0xCC, 0x8C, 0xC9, 0x03, 0x5A, 0xCC, + 0xA3, 0xB5, 0x03, 0x5A, 0xCC, 0xB1, 0xB5, 0x03, + 0x61, 0xCC, 0x80, 0xC9, 0x03, 0x61, 0xCC, 0x81, + // Bytes 3280 - 32bf + 0xC9, 0x03, 0x61, 0xCC, 0x83, 0xC9, 0x03, 0x61, + 0xCC, 0x84, 0xC9, 0x03, 0x61, 0xCC, 0x89, 0xC9, + 0x03, 0x61, 0xCC, 0x8C, 0xC9, 0x03, 0x61, 0xCC, + 0x8F, 0xC9, 0x03, 0x61, 0xCC, 0x91, 0xC9, 0x03, + 0x61, 0xCC, 0xA5, 0xB5, 0x03, 0x61, 0xCC, 0xA8, + 0xA5, 0x03, 0x62, 0xCC, 0x87, 0xC9, 0x03, 0x62, + 0xCC, 0xA3, 0xB5, 0x03, 0x62, 0xCC, 0xB1, 0xB5, + 0x03, 0x63, 0xCC, 0x81, 0xC9, 0x03, 0x63, 0xCC, + // Bytes 32c0 - 32ff + 0x82, 0xC9, 0x03, 0x63, 0xCC, 0x87, 0xC9, 0x03, + 0x63, 0xCC, 0x8C, 0xC9, 0x03, 0x64, 0xCC, 0x87, + 0xC9, 0x03, 0x64, 0xCC, 0x8C, 0xC9, 0x03, 0x64, + 0xCC, 0xA3, 0xB5, 0x03, 0x64, 0xCC, 0xA7, 0xA5, + 0x03, 0x64, 0xCC, 0xAD, 0xB5, 0x03, 0x64, 0xCC, + 0xB1, 0xB5, 0x03, 0x65, 0xCC, 0x80, 0xC9, 0x03, + 0x65, 0xCC, 0x81, 0xC9, 0x03, 0x65, 0xCC, 0x83, + 0xC9, 0x03, 0x65, 0xCC, 0x86, 0xC9, 0x03, 0x65, + // Bytes 3300 - 333f + 0xCC, 0x87, 0xC9, 0x03, 0x65, 0xCC, 0x88, 0xC9, + 0x03, 0x65, 0xCC, 0x89, 0xC9, 0x03, 0x65, 0xCC, + 0x8C, 0xC9, 0x03, 0x65, 0xCC, 0x8F, 0xC9, 0x03, + 0x65, 0xCC, 0x91, 0xC9, 0x03, 0x65, 0xCC, 0xA8, + 0xA5, 0x03, 0x65, 0xCC, 0xAD, 0xB5, 0x03, 0x65, + 0xCC, 0xB0, 0xB5, 0x03, 0x66, 0xCC, 0x87, 0xC9, + 0x03, 0x67, 0xCC, 0x81, 0xC9, 0x03, 0x67, 0xCC, + 0x82, 0xC9, 0x03, 0x67, 0xCC, 0x84, 0xC9, 0x03, + // Bytes 3340 - 337f + 0x67, 0xCC, 0x86, 0xC9, 0x03, 0x67, 0xCC, 0x87, + 0xC9, 0x03, 0x67, 0xCC, 0x8C, 0xC9, 0x03, 0x67, + 0xCC, 0xA7, 0xA5, 0x03, 0x68, 0xCC, 0x82, 0xC9, + 0x03, 0x68, 0xCC, 0x87, 0xC9, 0x03, 0x68, 0xCC, + 0x88, 0xC9, 0x03, 0x68, 0xCC, 0x8C, 0xC9, 0x03, + 0x68, 0xCC, 0xA3, 0xB5, 0x03, 0x68, 0xCC, 0xA7, + 0xA5, 0x03, 0x68, 0xCC, 0xAE, 0xB5, 0x03, 0x68, + 0xCC, 0xB1, 0xB5, 0x03, 0x69, 0xCC, 0x80, 0xC9, + // Bytes 3380 - 33bf + 0x03, 0x69, 0xCC, 0x81, 0xC9, 0x03, 0x69, 0xCC, + 0x82, 0xC9, 0x03, 0x69, 0xCC, 0x83, 0xC9, 0x03, + 0x69, 0xCC, 0x84, 0xC9, 0x03, 0x69, 0xCC, 0x86, + 0xC9, 0x03, 0x69, 0xCC, 0x89, 0xC9, 0x03, 0x69, + 0xCC, 0x8C, 0xC9, 0x03, 0x69, 0xCC, 0x8F, 0xC9, + 0x03, 0x69, 0xCC, 0x91, 0xC9, 0x03, 0x69, 0xCC, + 0xA3, 0xB5, 0x03, 0x69, 0xCC, 0xA8, 0xA5, 0x03, + 0x69, 0xCC, 0xB0, 0xB5, 0x03, 0x6A, 0xCC, 0x82, + // Bytes 33c0 - 33ff + 0xC9, 0x03, 0x6A, 0xCC, 0x8C, 0xC9, 0x03, 0x6B, + 0xCC, 0x81, 0xC9, 0x03, 0x6B, 0xCC, 0x8C, 0xC9, + 0x03, 0x6B, 0xCC, 0xA3, 0xB5, 0x03, 0x6B, 0xCC, + 0xA7, 0xA5, 0x03, 0x6B, 0xCC, 0xB1, 0xB5, 0x03, + 0x6C, 0xCC, 0x81, 0xC9, 0x03, 0x6C, 0xCC, 0x8C, + 0xC9, 0x03, 0x6C, 0xCC, 0xA7, 0xA5, 0x03, 0x6C, + 0xCC, 0xAD, 0xB5, 0x03, 0x6C, 0xCC, 0xB1, 0xB5, + 0x03, 0x6D, 0xCC, 0x81, 0xC9, 0x03, 0x6D, 0xCC, + // Bytes 3400 - 343f + 0x87, 0xC9, 0x03, 0x6D, 0xCC, 0xA3, 0xB5, 0x03, + 0x6E, 0xCC, 0x80, 0xC9, 0x03, 0x6E, 0xCC, 0x81, + 0xC9, 0x03, 0x6E, 0xCC, 0x83, 0xC9, 0x03, 0x6E, + 0xCC, 0x87, 0xC9, 0x03, 0x6E, 0xCC, 0x8C, 0xC9, + 0x03, 0x6E, 0xCC, 0xA3, 0xB5, 0x03, 0x6E, 0xCC, + 0xA7, 0xA5, 0x03, 0x6E, 0xCC, 0xAD, 0xB5, 0x03, + 0x6E, 0xCC, 0xB1, 0xB5, 0x03, 0x6F, 0xCC, 0x80, + 0xC9, 0x03, 0x6F, 0xCC, 0x81, 0xC9, 0x03, 0x6F, + // Bytes 3440 - 347f + 0xCC, 0x86, 0xC9, 0x03, 0x6F, 0xCC, 0x89, 0xC9, + 0x03, 0x6F, 0xCC, 0x8B, 0xC9, 0x03, 0x6F, 0xCC, + 0x8C, 0xC9, 0x03, 0x6F, 0xCC, 0x8F, 0xC9, 0x03, + 0x6F, 0xCC, 0x91, 0xC9, 0x03, 0x70, 0xCC, 0x81, + 0xC9, 0x03, 0x70, 0xCC, 0x87, 0xC9, 0x03, 0x72, + 0xCC, 0x81, 0xC9, 0x03, 0x72, 0xCC, 0x87, 0xC9, + 0x03, 0x72, 0xCC, 0x8C, 0xC9, 0x03, 0x72, 0xCC, + 0x8F, 0xC9, 0x03, 0x72, 0xCC, 0x91, 0xC9, 0x03, + // Bytes 3480 - 34bf + 0x72, 0xCC, 0xA7, 0xA5, 0x03, 0x72, 0xCC, 0xB1, + 0xB5, 0x03, 0x73, 0xCC, 0x82, 0xC9, 0x03, 0x73, + 0xCC, 0x87, 0xC9, 0x03, 0x73, 0xCC, 0xA6, 0xB5, + 0x03, 0x73, 0xCC, 0xA7, 0xA5, 0x03, 0x74, 0xCC, + 0x87, 0xC9, 0x03, 0x74, 0xCC, 0x88, 0xC9, 0x03, + 0x74, 0xCC, 0x8C, 0xC9, 0x03, 0x74, 0xCC, 0xA3, + 0xB5, 0x03, 0x74, 0xCC, 0xA6, 0xB5, 0x03, 0x74, + 0xCC, 0xA7, 0xA5, 0x03, 0x74, 0xCC, 0xAD, 0xB5, + // Bytes 34c0 - 34ff + 0x03, 0x74, 0xCC, 0xB1, 0xB5, 0x03, 0x75, 0xCC, + 0x80, 0xC9, 0x03, 0x75, 0xCC, 0x81, 0xC9, 0x03, + 0x75, 0xCC, 0x82, 0xC9, 0x03, 0x75, 0xCC, 0x86, + 0xC9, 0x03, 0x75, 0xCC, 0x89, 0xC9, 0x03, 0x75, + 0xCC, 0x8A, 0xC9, 0x03, 0x75, 0xCC, 0x8B, 0xC9, + 0x03, 0x75, 0xCC, 0x8C, 0xC9, 0x03, 0x75, 0xCC, + 0x8F, 0xC9, 0x03, 0x75, 0xCC, 0x91, 0xC9, 0x03, + 0x75, 0xCC, 0xA3, 0xB5, 0x03, 0x75, 0xCC, 0xA4, + // Bytes 3500 - 353f + 0xB5, 0x03, 0x75, 0xCC, 0xA8, 0xA5, 0x03, 0x75, + 0xCC, 0xAD, 0xB5, 0x03, 0x75, 0xCC, 0xB0, 0xB5, + 0x03, 0x76, 0xCC, 0x83, 0xC9, 0x03, 0x76, 0xCC, + 0xA3, 0xB5, 0x03, 0x77, 0xCC, 0x80, 0xC9, 0x03, + 0x77, 0xCC, 0x81, 0xC9, 0x03, 0x77, 0xCC, 0x82, + 0xC9, 0x03, 0x77, 0xCC, 0x87, 0xC9, 0x03, 0x77, + 0xCC, 0x88, 0xC9, 0x03, 0x77, 0xCC, 0x8A, 0xC9, + 0x03, 0x77, 0xCC, 0xA3, 0xB5, 0x03, 0x78, 0xCC, + // Bytes 3540 - 357f + 0x87, 0xC9, 0x03, 0x78, 0xCC, 0x88, 0xC9, 0x03, + 0x79, 0xCC, 0x80, 0xC9, 0x03, 0x79, 0xCC, 0x81, + 0xC9, 0x03, 0x79, 0xCC, 0x82, 0xC9, 0x03, 0x79, + 0xCC, 0x83, 0xC9, 0x03, 0x79, 0xCC, 0x84, 0xC9, + 0x03, 0x79, 0xCC, 0x87, 0xC9, 0x03, 0x79, 0xCC, + 0x88, 0xC9, 0x03, 0x79, 0xCC, 0x89, 0xC9, 0x03, + 0x79, 0xCC, 0x8A, 0xC9, 0x03, 0x79, 0xCC, 0xA3, + 0xB5, 0x03, 0x7A, 0xCC, 0x81, 0xC9, 0x03, 0x7A, + // Bytes 3580 - 35bf + 0xCC, 0x82, 0xC9, 0x03, 0x7A, 0xCC, 0x87, 0xC9, + 0x03, 0x7A, 0xCC, 0x8C, 0xC9, 0x03, 0x7A, 0xCC, + 0xA3, 0xB5, 0x03, 0x7A, 0xCC, 0xB1, 0xB5, 0x04, + 0xC2, 0xA8, 0xCC, 0x80, 0xCA, 0x04, 0xC2, 0xA8, + 0xCC, 0x81, 0xCA, 0x04, 0xC2, 0xA8, 0xCD, 0x82, + 0xCA, 0x04, 0xC3, 0x86, 0xCC, 0x81, 0xC9, 0x04, + 0xC3, 0x86, 0xCC, 0x84, 0xC9, 0x04, 0xC3, 0x98, + 0xCC, 0x81, 0xC9, 0x04, 0xC3, 0xA6, 0xCC, 0x81, + // Bytes 35c0 - 35ff + 0xC9, 0x04, 0xC3, 0xA6, 0xCC, 0x84, 0xC9, 0x04, + 0xC3, 0xB8, 0xCC, 0x81, 0xC9, 0x04, 0xC5, 0xBF, + 0xCC, 0x87, 0xC9, 0x04, 0xC6, 0xB7, 0xCC, 0x8C, + 0xC9, 0x04, 0xCA, 0x92, 0xCC, 0x8C, 0xC9, 0x04, + 0xCE, 0x91, 0xCC, 0x80, 0xC9, 0x04, 0xCE, 0x91, + 0xCC, 0x81, 0xC9, 0x04, 0xCE, 0x91, 0xCC, 0x84, + 0xC9, 0x04, 0xCE, 0x91, 0xCC, 0x86, 0xC9, 0x04, + 0xCE, 0x91, 0xCD, 0x85, 0xD9, 0x04, 0xCE, 0x95, + // Bytes 3600 - 363f + 0xCC, 0x80, 0xC9, 0x04, 0xCE, 0x95, 0xCC, 0x81, + 0xC9, 0x04, 0xCE, 0x97, 0xCC, 0x80, 0xC9, 0x04, + 0xCE, 0x97, 0xCC, 0x81, 0xC9, 0x04, 0xCE, 0x97, + 0xCD, 0x85, 0xD9, 0x04, 0xCE, 0x99, 0xCC, 0x80, + 0xC9, 0x04, 0xCE, 0x99, 0xCC, 0x81, 0xC9, 0x04, + 0xCE, 0x99, 0xCC, 0x84, 0xC9, 0x04, 0xCE, 0x99, + 0xCC, 0x86, 0xC9, 0x04, 0xCE, 0x99, 0xCC, 0x88, + 0xC9, 0x04, 0xCE, 0x9F, 0xCC, 0x80, 0xC9, 0x04, + // Bytes 3640 - 367f + 0xCE, 0x9F, 0xCC, 0x81, 0xC9, 0x04, 0xCE, 0xA1, + 0xCC, 0x94, 0xC9, 0x04, 0xCE, 0xA5, 0xCC, 0x80, + 0xC9, 0x04, 0xCE, 0xA5, 0xCC, 0x81, 0xC9, 0x04, + 0xCE, 0xA5, 0xCC, 0x84, 0xC9, 0x04, 0xCE, 0xA5, + 0xCC, 0x86, 0xC9, 0x04, 0xCE, 0xA5, 0xCC, 0x88, + 0xC9, 0x04, 0xCE, 0xA9, 0xCC, 0x80, 0xC9, 0x04, + 0xCE, 0xA9, 0xCC, 0x81, 0xC9, 0x04, 0xCE, 0xA9, + 0xCD, 0x85, 0xD9, 0x04, 0xCE, 0xB1, 0xCC, 0x84, + // Bytes 3680 - 36bf + 0xC9, 0x04, 0xCE, 0xB1, 0xCC, 0x86, 0xC9, 0x04, + 0xCE, 0xB1, 0xCD, 0x85, 0xD9, 0x04, 0xCE, 0xB5, + 0xCC, 0x80, 0xC9, 0x04, 0xCE, 0xB5, 0xCC, 0x81, + 0xC9, 0x04, 0xCE, 0xB7, 0xCD, 0x85, 0xD9, 0x04, + 0xCE, 0xB9, 0xCC, 0x80, 0xC9, 0x04, 0xCE, 0xB9, + 0xCC, 0x81, 0xC9, 0x04, 0xCE, 0xB9, 0xCC, 0x84, + 0xC9, 0x04, 0xCE, 0xB9, 0xCC, 0x86, 0xC9, 0x04, + 0xCE, 0xB9, 0xCD, 0x82, 0xC9, 0x04, 0xCE, 0xBF, + // Bytes 36c0 - 36ff + 0xCC, 0x80, 0xC9, 0x04, 0xCE, 0xBF, 0xCC, 0x81, + 0xC9, 0x04, 0xCF, 0x81, 0xCC, 0x93, 0xC9, 0x04, + 0xCF, 0x81, 0xCC, 0x94, 0xC9, 0x04, 0xCF, 0x85, + 0xCC, 0x80, 0xC9, 0x04, 0xCF, 0x85, 0xCC, 0x81, + 0xC9, 0x04, 0xCF, 0x85, 0xCC, 0x84, 0xC9, 0x04, + 0xCF, 0x85, 0xCC, 0x86, 0xC9, 0x04, 0xCF, 0x85, + 0xCD, 0x82, 0xC9, 0x04, 0xCF, 0x89, 0xCD, 0x85, + 0xD9, 0x04, 0xCF, 0x92, 0xCC, 0x81, 0xC9, 0x04, + // Bytes 3700 - 373f + 0xCF, 0x92, 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0x86, + 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0x90, 0xCC, 0x86, + 0xC9, 0x04, 0xD0, 0x90, 0xCC, 0x88, 0xC9, 0x04, + 0xD0, 0x93, 0xCC, 0x81, 0xC9, 0x04, 0xD0, 0x95, + 0xCC, 0x80, 0xC9, 0x04, 0xD0, 0x95, 0xCC, 0x86, + 0xC9, 0x04, 0xD0, 0x95, 0xCC, 0x88, 0xC9, 0x04, + 0xD0, 0x96, 0xCC, 0x86, 0xC9, 0x04, 0xD0, 0x96, + 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0x97, 0xCC, 0x88, + // Bytes 3740 - 377f + 0xC9, 0x04, 0xD0, 0x98, 0xCC, 0x80, 0xC9, 0x04, + 0xD0, 0x98, 0xCC, 0x84, 0xC9, 0x04, 0xD0, 0x98, + 0xCC, 0x86, 0xC9, 0x04, 0xD0, 0x98, 0xCC, 0x88, + 0xC9, 0x04, 0xD0, 0x9A, 0xCC, 0x81, 0xC9, 0x04, + 0xD0, 0x9E, 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xA3, + 0xCC, 0x84, 0xC9, 0x04, 0xD0, 0xA3, 0xCC, 0x86, + 0xC9, 0x04, 0xD0, 0xA3, 0xCC, 0x88, 0xC9, 0x04, + 0xD0, 0xA3, 0xCC, 0x8B, 0xC9, 0x04, 0xD0, 0xA7, + // Bytes 3780 - 37bf + 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xAB, 0xCC, 0x88, + 0xC9, 0x04, 0xD0, 0xAD, 0xCC, 0x88, 0xC9, 0x04, + 0xD0, 0xB0, 0xCC, 0x86, 0xC9, 0x04, 0xD0, 0xB0, + 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xB3, 0xCC, 0x81, + 0xC9, 0x04, 0xD0, 0xB5, 0xCC, 0x80, 0xC9, 0x04, + 0xD0, 0xB5, 0xCC, 0x86, 0xC9, 0x04, 0xD0, 0xB5, + 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xB6, 0xCC, 0x86, + 0xC9, 0x04, 0xD0, 0xB6, 0xCC, 0x88, 0xC9, 0x04, + // Bytes 37c0 - 37ff + 0xD0, 0xB7, 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xB8, + 0xCC, 0x80, 0xC9, 0x04, 0xD0, 0xB8, 0xCC, 0x84, + 0xC9, 0x04, 0xD0, 0xB8, 0xCC, 0x86, 0xC9, 0x04, + 0xD0, 0xB8, 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xBA, + 0xCC, 0x81, 0xC9, 0x04, 0xD0, 0xBE, 0xCC, 0x88, + 0xC9, 0x04, 0xD1, 0x83, 0xCC, 0x84, 0xC9, 0x04, + 0xD1, 0x83, 0xCC, 0x86, 0xC9, 0x04, 0xD1, 0x83, + 0xCC, 0x88, 0xC9, 0x04, 0xD1, 0x83, 0xCC, 0x8B, + // Bytes 3800 - 383f + 0xC9, 0x04, 0xD1, 0x87, 0xCC, 0x88, 0xC9, 0x04, + 0xD1, 0x8B, 0xCC, 0x88, 0xC9, 0x04, 0xD1, 0x8D, + 0xCC, 0x88, 0xC9, 0x04, 0xD1, 0x96, 0xCC, 0x88, + 0xC9, 0x04, 0xD1, 0xB4, 0xCC, 0x8F, 0xC9, 0x04, + 0xD1, 0xB5, 0xCC, 0x8F, 0xC9, 0x04, 0xD3, 0x98, + 0xCC, 0x88, 0xC9, 0x04, 0xD3, 0x99, 0xCC, 0x88, + 0xC9, 0x04, 0xD3, 0xA8, 0xCC, 0x88, 0xC9, 0x04, + 0xD3, 0xA9, 0xCC, 0x88, 0xC9, 0x04, 0xD8, 0xA7, + // Bytes 3840 - 387f + 0xD9, 0x93, 0xC9, 0x04, 0xD8, 0xA7, 0xD9, 0x94, + 0xC9, 0x04, 0xD8, 0xA7, 0xD9, 0x95, 0xB5, 0x04, + 0xD9, 0x88, 0xD9, 0x94, 0xC9, 0x04, 0xD9, 0x8A, + 0xD9, 0x94, 0xC9, 0x04, 0xDB, 0x81, 0xD9, 0x94, + 0xC9, 0x04, 0xDB, 0x92, 0xD9, 0x94, 0xC9, 0x04, + 0xDB, 0x95, 0xD9, 0x94, 0xC9, 0x05, 0x41, 0xCC, + 0x82, 0xCC, 0x80, 0xCA, 0x05, 0x41, 0xCC, 0x82, + 0xCC, 0x81, 0xCA, 0x05, 0x41, 0xCC, 0x82, 0xCC, + // Bytes 3880 - 38bf + 0x83, 0xCA, 0x05, 0x41, 0xCC, 0x82, 0xCC, 0x89, + 0xCA, 0x05, 0x41, 0xCC, 0x86, 0xCC, 0x80, 0xCA, + 0x05, 0x41, 0xCC, 0x86, 0xCC, 0x81, 0xCA, 0x05, + 0x41, 0xCC, 0x86, 0xCC, 0x83, 0xCA, 0x05, 0x41, + 0xCC, 0x86, 0xCC, 0x89, 0xCA, 0x05, 0x41, 0xCC, + 0x87, 0xCC, 0x84, 0xCA, 0x05, 0x41, 0xCC, 0x88, + 0xCC, 0x84, 0xCA, 0x05, 0x41, 0xCC, 0x8A, 0xCC, + 0x81, 0xCA, 0x05, 0x41, 0xCC, 0xA3, 0xCC, 0x82, + // Bytes 38c0 - 38ff + 0xCA, 0x05, 0x41, 0xCC, 0xA3, 0xCC, 0x86, 0xCA, + 0x05, 0x43, 0xCC, 0xA7, 0xCC, 0x81, 0xCA, 0x05, + 0x45, 0xCC, 0x82, 0xCC, 0x80, 0xCA, 0x05, 0x45, + 0xCC, 0x82, 0xCC, 0x81, 0xCA, 0x05, 0x45, 0xCC, + 0x82, 0xCC, 0x83, 0xCA, 0x05, 0x45, 0xCC, 0x82, + 0xCC, 0x89, 0xCA, 0x05, 0x45, 0xCC, 0x84, 0xCC, + 0x80, 0xCA, 0x05, 0x45, 0xCC, 0x84, 0xCC, 0x81, + 0xCA, 0x05, 0x45, 0xCC, 0xA3, 0xCC, 0x82, 0xCA, + // Bytes 3900 - 393f + 0x05, 0x45, 0xCC, 0xA7, 0xCC, 0x86, 0xCA, 0x05, + 0x49, 0xCC, 0x88, 0xCC, 0x81, 0xCA, 0x05, 0x4C, + 0xCC, 0xA3, 0xCC, 0x84, 0xCA, 0x05, 0x4F, 0xCC, + 0x82, 0xCC, 0x80, 0xCA, 0x05, 0x4F, 0xCC, 0x82, + 0xCC, 0x81, 0xCA, 0x05, 0x4F, 0xCC, 0x82, 0xCC, + 0x83, 0xCA, 0x05, 0x4F, 0xCC, 0x82, 0xCC, 0x89, + 0xCA, 0x05, 0x4F, 0xCC, 0x83, 0xCC, 0x81, 0xCA, + 0x05, 0x4F, 0xCC, 0x83, 0xCC, 0x84, 0xCA, 0x05, + // Bytes 3940 - 397f + 0x4F, 0xCC, 0x83, 0xCC, 0x88, 0xCA, 0x05, 0x4F, + 0xCC, 0x84, 0xCC, 0x80, 0xCA, 0x05, 0x4F, 0xCC, + 0x84, 0xCC, 0x81, 0xCA, 0x05, 0x4F, 0xCC, 0x87, + 0xCC, 0x84, 0xCA, 0x05, 0x4F, 0xCC, 0x88, 0xCC, + 0x84, 0xCA, 0x05, 0x4F, 0xCC, 0x9B, 0xCC, 0x80, + 0xCA, 0x05, 0x4F, 0xCC, 0x9B, 0xCC, 0x81, 0xCA, + 0x05, 0x4F, 0xCC, 0x9B, 0xCC, 0x83, 0xCA, 0x05, + 0x4F, 0xCC, 0x9B, 0xCC, 0x89, 0xCA, 0x05, 0x4F, + // Bytes 3980 - 39bf + 0xCC, 0x9B, 0xCC, 0xA3, 0xB6, 0x05, 0x4F, 0xCC, + 0xA3, 0xCC, 0x82, 0xCA, 0x05, 0x4F, 0xCC, 0xA8, + 0xCC, 0x84, 0xCA, 0x05, 0x52, 0xCC, 0xA3, 0xCC, + 0x84, 0xCA, 0x05, 0x53, 0xCC, 0x81, 0xCC, 0x87, + 0xCA, 0x05, 0x53, 0xCC, 0x8C, 0xCC, 0x87, 0xCA, + 0x05, 0x53, 0xCC, 0xA3, 0xCC, 0x87, 0xCA, 0x05, + 0x55, 0xCC, 0x83, 0xCC, 0x81, 0xCA, 0x05, 0x55, + 0xCC, 0x84, 0xCC, 0x88, 0xCA, 0x05, 0x55, 0xCC, + // Bytes 39c0 - 39ff + 0x88, 0xCC, 0x80, 0xCA, 0x05, 0x55, 0xCC, 0x88, + 0xCC, 0x81, 0xCA, 0x05, 0x55, 0xCC, 0x88, 0xCC, + 0x84, 0xCA, 0x05, 0x55, 0xCC, 0x88, 0xCC, 0x8C, + 0xCA, 0x05, 0x55, 0xCC, 0x9B, 0xCC, 0x80, 0xCA, + 0x05, 0x55, 0xCC, 0x9B, 0xCC, 0x81, 0xCA, 0x05, + 0x55, 0xCC, 0x9B, 0xCC, 0x83, 0xCA, 0x05, 0x55, + 0xCC, 0x9B, 0xCC, 0x89, 0xCA, 0x05, 0x55, 0xCC, + 0x9B, 0xCC, 0xA3, 0xB6, 0x05, 0x61, 0xCC, 0x82, + // Bytes 3a00 - 3a3f + 0xCC, 0x80, 0xCA, 0x05, 0x61, 0xCC, 0x82, 0xCC, + 0x81, 0xCA, 0x05, 0x61, 0xCC, 0x82, 0xCC, 0x83, + 0xCA, 0x05, 0x61, 0xCC, 0x82, 0xCC, 0x89, 0xCA, + 0x05, 0x61, 0xCC, 0x86, 0xCC, 0x80, 0xCA, 0x05, + 0x61, 0xCC, 0x86, 0xCC, 0x81, 0xCA, 0x05, 0x61, + 0xCC, 0x86, 0xCC, 0x83, 0xCA, 0x05, 0x61, 0xCC, + 0x86, 0xCC, 0x89, 0xCA, 0x05, 0x61, 0xCC, 0x87, + 0xCC, 0x84, 0xCA, 0x05, 0x61, 0xCC, 0x88, 0xCC, + // Bytes 3a40 - 3a7f + 0x84, 0xCA, 0x05, 0x61, 0xCC, 0x8A, 0xCC, 0x81, + 0xCA, 0x05, 0x61, 0xCC, 0xA3, 0xCC, 0x82, 0xCA, + 0x05, 0x61, 0xCC, 0xA3, 0xCC, 0x86, 0xCA, 0x05, + 0x63, 0xCC, 0xA7, 0xCC, 0x81, 0xCA, 0x05, 0x65, + 0xCC, 0x82, 0xCC, 0x80, 0xCA, 0x05, 0x65, 0xCC, + 0x82, 0xCC, 0x81, 0xCA, 0x05, 0x65, 0xCC, 0x82, + 0xCC, 0x83, 0xCA, 0x05, 0x65, 0xCC, 0x82, 0xCC, + 0x89, 0xCA, 0x05, 0x65, 0xCC, 0x84, 0xCC, 0x80, + // Bytes 3a80 - 3abf + 0xCA, 0x05, 0x65, 0xCC, 0x84, 0xCC, 0x81, 0xCA, + 0x05, 0x65, 0xCC, 0xA3, 0xCC, 0x82, 0xCA, 0x05, + 0x65, 0xCC, 0xA7, 0xCC, 0x86, 0xCA, 0x05, 0x69, + 0xCC, 0x88, 0xCC, 0x81, 0xCA, 0x05, 0x6C, 0xCC, + 0xA3, 0xCC, 0x84, 0xCA, 0x05, 0x6F, 0xCC, 0x82, + 0xCC, 0x80, 0xCA, 0x05, 0x6F, 0xCC, 0x82, 0xCC, + 0x81, 0xCA, 0x05, 0x6F, 0xCC, 0x82, 0xCC, 0x83, + 0xCA, 0x05, 0x6F, 0xCC, 0x82, 0xCC, 0x89, 0xCA, + // Bytes 3ac0 - 3aff + 0x05, 0x6F, 0xCC, 0x83, 0xCC, 0x81, 0xCA, 0x05, + 0x6F, 0xCC, 0x83, 0xCC, 0x84, 0xCA, 0x05, 0x6F, + 0xCC, 0x83, 0xCC, 0x88, 0xCA, 0x05, 0x6F, 0xCC, + 0x84, 0xCC, 0x80, 0xCA, 0x05, 0x6F, 0xCC, 0x84, + 0xCC, 0x81, 0xCA, 0x05, 0x6F, 0xCC, 0x87, 0xCC, + 0x84, 0xCA, 0x05, 0x6F, 0xCC, 0x88, 0xCC, 0x84, + 0xCA, 0x05, 0x6F, 0xCC, 0x9B, 0xCC, 0x80, 0xCA, + 0x05, 0x6F, 0xCC, 0x9B, 0xCC, 0x81, 0xCA, 0x05, + // Bytes 3b00 - 3b3f + 0x6F, 0xCC, 0x9B, 0xCC, 0x83, 0xCA, 0x05, 0x6F, + 0xCC, 0x9B, 0xCC, 0x89, 0xCA, 0x05, 0x6F, 0xCC, + 0x9B, 0xCC, 0xA3, 0xB6, 0x05, 0x6F, 0xCC, 0xA3, + 0xCC, 0x82, 0xCA, 0x05, 0x6F, 0xCC, 0xA8, 0xCC, + 0x84, 0xCA, 0x05, 0x72, 0xCC, 0xA3, 0xCC, 0x84, + 0xCA, 0x05, 0x73, 0xCC, 0x81, 0xCC, 0x87, 0xCA, + 0x05, 0x73, 0xCC, 0x8C, 0xCC, 0x87, 0xCA, 0x05, + 0x73, 0xCC, 0xA3, 0xCC, 0x87, 0xCA, 0x05, 0x75, + // Bytes 3b40 - 3b7f + 0xCC, 0x83, 0xCC, 0x81, 0xCA, 0x05, 0x75, 0xCC, + 0x84, 0xCC, 0x88, 0xCA, 0x05, 0x75, 0xCC, 0x88, + 0xCC, 0x80, 0xCA, 0x05, 0x75, 0xCC, 0x88, 0xCC, + 0x81, 0xCA, 0x05, 0x75, 0xCC, 0x88, 0xCC, 0x84, + 0xCA, 0x05, 0x75, 0xCC, 0x88, 0xCC, 0x8C, 0xCA, + 0x05, 0x75, 0xCC, 0x9B, 0xCC, 0x80, 0xCA, 0x05, + 0x75, 0xCC, 0x9B, 0xCC, 0x81, 0xCA, 0x05, 0x75, + 0xCC, 0x9B, 0xCC, 0x83, 0xCA, 0x05, 0x75, 0xCC, + // Bytes 3b80 - 3bbf + 0x9B, 0xCC, 0x89, 0xCA, 0x05, 0x75, 0xCC, 0x9B, + 0xCC, 0xA3, 0xB6, 0x05, 0xE1, 0xBE, 0xBF, 0xCC, + 0x80, 0xCA, 0x05, 0xE1, 0xBE, 0xBF, 0xCC, 0x81, + 0xCA, 0x05, 0xE1, 0xBE, 0xBF, 0xCD, 0x82, 0xCA, + 0x05, 0xE1, 0xBF, 0xBE, 0xCC, 0x80, 0xCA, 0x05, + 0xE1, 0xBF, 0xBE, 0xCC, 0x81, 0xCA, 0x05, 0xE1, + 0xBF, 0xBE, 0xCD, 0x82, 0xCA, 0x05, 0xE2, 0x86, + 0x90, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x86, 0x92, + // Bytes 3bc0 - 3bff + 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x86, 0x94, 0xCC, + 0xB8, 0x05, 0x05, 0xE2, 0x87, 0x90, 0xCC, 0xB8, + 0x05, 0x05, 0xE2, 0x87, 0x92, 0xCC, 0xB8, 0x05, + 0x05, 0xE2, 0x87, 0x94, 0xCC, 0xB8, 0x05, 0x05, + 0xE2, 0x88, 0x83, 0xCC, 0xB8, 0x05, 0x05, 0xE2, + 0x88, 0x88, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x88, + 0x8B, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x88, 0xA3, + 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x88, 0xA5, 0xCC, + // Bytes 3c00 - 3c3f + 0xB8, 0x05, 0x05, 0xE2, 0x88, 0xBC, 0xCC, 0xB8, + 0x05, 0x05, 0xE2, 0x89, 0x83, 0xCC, 0xB8, 0x05, + 0x05, 0xE2, 0x89, 0x85, 0xCC, 0xB8, 0x05, 0x05, + 0xE2, 0x89, 0x88, 0xCC, 0xB8, 0x05, 0x05, 0xE2, + 0x89, 0x8D, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, + 0xA1, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, 0xA4, + 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, 0xA5, 0xCC, + 0xB8, 0x05, 0x05, 0xE2, 0x89, 0xB2, 0xCC, 0xB8, + // Bytes 3c40 - 3c7f + 0x05, 0x05, 0xE2, 0x89, 0xB3, 0xCC, 0xB8, 0x05, + 0x05, 0xE2, 0x89, 0xB6, 0xCC, 0xB8, 0x05, 0x05, + 0xE2, 0x89, 0xB7, 0xCC, 0xB8, 0x05, 0x05, 0xE2, + 0x89, 0xBA, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, + 0xBB, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, 0xBC, + 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, 0xBD, 0xCC, + 0xB8, 0x05, 0x05, 0xE2, 0x8A, 0x82, 0xCC, 0xB8, + 0x05, 0x05, 0xE2, 0x8A, 0x83, 0xCC, 0xB8, 0x05, + // Bytes 3c80 - 3cbf + 0x05, 0xE2, 0x8A, 0x86, 0xCC, 0xB8, 0x05, 0x05, + 0xE2, 0x8A, 0x87, 0xCC, 0xB8, 0x05, 0x05, 0xE2, + 0x8A, 0x91, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A, + 0x92, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A, 0xA2, + 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A, 0xA8, 0xCC, + 0xB8, 0x05, 0x05, 0xE2, 0x8A, 0xA9, 0xCC, 0xB8, + 0x05, 0x05, 0xE2, 0x8A, 0xAB, 0xCC, 0xB8, 0x05, + 0x05, 0xE2, 0x8A, 0xB2, 0xCC, 0xB8, 0x05, 0x05, + // Bytes 3cc0 - 3cff + 0xE2, 0x8A, 0xB3, 0xCC, 0xB8, 0x05, 0x05, 0xE2, + 0x8A, 0xB4, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A, + 0xB5, 0xCC, 0xB8, 0x05, 0x06, 0xCE, 0x91, 0xCC, + 0x93, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0x91, 0xCC, + 0x94, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0x95, 0xCC, + 0x93, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0x95, 0xCC, + 0x93, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0x95, 0xCC, + 0x94, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0x95, 0xCC, + // Bytes 3d00 - 3d3f + 0x94, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0x97, 0xCC, + 0x93, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0x97, 0xCC, + 0x94, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0x99, 0xCC, + 0x93, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0x99, 0xCC, + 0x93, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0x99, 0xCC, + 0x93, 0xCD, 0x82, 0xCA, 0x06, 0xCE, 0x99, 0xCC, + 0x94, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0x99, 0xCC, + 0x94, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0x99, 0xCC, + // Bytes 3d40 - 3d7f + 0x94, 0xCD, 0x82, 0xCA, 0x06, 0xCE, 0x9F, 0xCC, + 0x93, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0x9F, 0xCC, + 0x93, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0x9F, 0xCC, + 0x94, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0x9F, 0xCC, + 0x94, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0xA5, 0xCC, + 0x94, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0xA5, 0xCC, + 0x94, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0xA5, 0xCC, + 0x94, 0xCD, 0x82, 0xCA, 0x06, 0xCE, 0xA9, 0xCC, + // Bytes 3d80 - 3dbf + 0x93, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xA9, 0xCC, + 0x94, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB1, 0xCC, + 0x80, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB1, 0xCC, + 0x81, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB1, 0xCC, + 0x93, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB1, 0xCC, + 0x94, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB1, 0xCD, + 0x82, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB5, 0xCC, + 0x93, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0xB5, 0xCC, + // Bytes 3dc0 - 3dff + 0x93, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0xB5, 0xCC, + 0x94, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0xB5, 0xCC, + 0x94, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0xB7, 0xCC, + 0x80, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB7, 0xCC, + 0x81, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB7, 0xCC, + 0x93, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB7, 0xCC, + 0x94, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB7, 0xCD, + 0x82, 0xCD, 0x85, 0xDA, 0x06, 0xCE, 0xB9, 0xCC, + // Bytes 3e00 - 3e3f + 0x88, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0xB9, 0xCC, + 0x88, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0xB9, 0xCC, + 0x88, 0xCD, 0x82, 0xCA, 0x06, 0xCE, 0xB9, 0xCC, + 0x93, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0xB9, 0xCC, + 0x93, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0xB9, 0xCC, + 0x93, 0xCD, 0x82, 0xCA, 0x06, 0xCE, 0xB9, 0xCC, + 0x94, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0xB9, 0xCC, + 0x94, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0xB9, 0xCC, + // Bytes 3e40 - 3e7f + 0x94, 0xCD, 0x82, 0xCA, 0x06, 0xCE, 0xBF, 0xCC, + 0x93, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0xBF, 0xCC, + 0x93, 0xCC, 0x81, 0xCA, 0x06, 0xCE, 0xBF, 0xCC, + 0x94, 0xCC, 0x80, 0xCA, 0x06, 0xCE, 0xBF, 0xCC, + 0x94, 0xCC, 0x81, 0xCA, 0x06, 0xCF, 0x85, 0xCC, + 0x88, 0xCC, 0x80, 0xCA, 0x06, 0xCF, 0x85, 0xCC, + 0x88, 0xCC, 0x81, 0xCA, 0x06, 0xCF, 0x85, 0xCC, + 0x88, 0xCD, 0x82, 0xCA, 0x06, 0xCF, 0x85, 0xCC, + // Bytes 3e80 - 3ebf + 0x93, 0xCC, 0x80, 0xCA, 0x06, 0xCF, 0x85, 0xCC, + 0x93, 0xCC, 0x81, 0xCA, 0x06, 0xCF, 0x85, 0xCC, + 0x93, 0xCD, 0x82, 0xCA, 0x06, 0xCF, 0x85, 0xCC, + 0x94, 0xCC, 0x80, 0xCA, 0x06, 0xCF, 0x85, 0xCC, + 0x94, 0xCC, 0x81, 0xCA, 0x06, 0xCF, 0x85, 0xCC, + 0x94, 0xCD, 0x82, 0xCA, 0x06, 0xCF, 0x89, 0xCC, + 0x80, 0xCD, 0x85, 0xDA, 0x06, 0xCF, 0x89, 0xCC, + 0x81, 0xCD, 0x85, 0xDA, 0x06, 0xCF, 0x89, 0xCC, + // Bytes 3ec0 - 3eff + 0x93, 0xCD, 0x85, 0xDA, 0x06, 0xCF, 0x89, 0xCC, + 0x94, 0xCD, 0x85, 0xDA, 0x06, 0xCF, 0x89, 0xCD, + 0x82, 0xCD, 0x85, 0xDA, 0x06, 0xE0, 0xA4, 0xA8, + 0xE0, 0xA4, 0xBC, 0x09, 0x06, 0xE0, 0xA4, 0xB0, + 0xE0, 0xA4, 0xBC, 0x09, 0x06, 0xE0, 0xA4, 0xB3, + 0xE0, 0xA4, 0xBC, 0x09, 0x06, 0xE0, 0xB1, 0x86, + 0xE0, 0xB1, 0x96, 0x85, 0x06, 0xE0, 0xB7, 0x99, + 0xE0, 0xB7, 0x8A, 0x11, 0x06, 0xE3, 0x81, 0x86, + // Bytes 3f00 - 3f3f + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x8B, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x8D, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x8F, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x91, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x93, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x95, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x97, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x99, + // Bytes 3f40 - 3f7f + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x9B, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x9D, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0x9F, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xA1, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xA4, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xA6, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xA8, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xAF, + // Bytes 3f80 - 3fbf + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xAF, + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x81, 0xB2, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xB2, + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x81, 0xB5, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xB5, + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x81, 0xB8, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xB8, + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x81, 0xBB, + // Bytes 3fc0 - 3fff + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x81, 0xBB, + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x82, 0x9D, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xA6, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xAB, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xAD, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xAF, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xB1, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xB3, + // Bytes 4000 - 403f + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xB5, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xB7, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xB9, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xBB, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xBD, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x82, 0xBF, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x81, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x84, + // Bytes 4040 - 407f + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x86, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x88, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x8F, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x8F, + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x83, 0x92, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x92, + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x83, 0x95, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x95, + // Bytes 4080 - 40bf + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x83, 0x98, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x98, + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x83, 0x9B, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0x9B, + 0xE3, 0x82, 0x9A, 0x0D, 0x06, 0xE3, 0x83, 0xAF, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0xB0, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0xB1, + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0xB2, + // Bytes 40c0 - 40ff + 0xE3, 0x82, 0x99, 0x0D, 0x06, 0xE3, 0x83, 0xBD, + 0xE3, 0x82, 0x99, 0x0D, 0x08, 0xCE, 0x91, 0xCC, + 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE, + 0x91, 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xDB, + 0x08, 0xCE, 0x91, 0xCC, 0x93, 0xCD, 0x82, 0xCD, + 0x85, 0xDB, 0x08, 0xCE, 0x91, 0xCC, 0x94, 0xCC, + 0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0x91, 0xCC, + 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE, + // Bytes 4100 - 413f + 0x91, 0xCC, 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xDB, + 0x08, 0xCE, 0x97, 0xCC, 0x93, 0xCC, 0x80, 0xCD, + 0x85, 0xDB, 0x08, 0xCE, 0x97, 0xCC, 0x93, 0xCC, + 0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0x97, 0xCC, + 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCE, + 0x97, 0xCC, 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xDB, + 0x08, 0xCE, 0x97, 0xCC, 0x94, 0xCC, 0x81, 0xCD, + 0x85, 0xDB, 0x08, 0xCE, 0x97, 0xCC, 0x94, 0xCD, + // Bytes 4140 - 417f + 0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0xA9, 0xCC, + 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE, + 0xA9, 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xDB, + 0x08, 0xCE, 0xA9, 0xCC, 0x93, 0xCD, 0x82, 0xCD, + 0x85, 0xDB, 0x08, 0xCE, 0xA9, 0xCC, 0x94, 0xCC, + 0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0xA9, 0xCC, + 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE, + 0xA9, 0xCC, 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xDB, + // Bytes 4180 - 41bf + 0x08, 0xCE, 0xB1, 0xCC, 0x93, 0xCC, 0x80, 0xCD, + 0x85, 0xDB, 0x08, 0xCE, 0xB1, 0xCC, 0x93, 0xCC, + 0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0xB1, 0xCC, + 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCE, + 0xB1, 0xCC, 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xDB, + 0x08, 0xCE, 0xB1, 0xCC, 0x94, 0xCC, 0x81, 0xCD, + 0x85, 0xDB, 0x08, 0xCE, 0xB1, 0xCC, 0x94, 0xCD, + 0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0xB7, 0xCC, + // Bytes 41c0 - 41ff + 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE, + 0xB7, 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xDB, + 0x08, 0xCE, 0xB7, 0xCC, 0x93, 0xCD, 0x82, 0xCD, + 0x85, 0xDB, 0x08, 0xCE, 0xB7, 0xCC, 0x94, 0xCC, + 0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0xB7, 0xCC, + 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE, + 0xB7, 0xCC, 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xDB, + 0x08, 0xCF, 0x89, 0xCC, 0x93, 0xCC, 0x80, 0xCD, + // Bytes 4200 - 423f + 0x85, 0xDB, 0x08, 0xCF, 0x89, 0xCC, 0x93, 0xCC, + 0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCF, 0x89, 0xCC, + 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCF, + 0x89, 0xCC, 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xDB, + 0x08, 0xCF, 0x89, 0xCC, 0x94, 0xCC, 0x81, 0xCD, + 0x85, 0xDB, 0x08, 0xCF, 0x89, 0xCC, 0x94, 0xCD, + 0x82, 0xCD, 0x85, 0xDB, 0x08, 0xF0, 0x91, 0x82, + 0x99, 0xF0, 0x91, 0x82, 0xBA, 0x09, 0x08, 0xF0, + // Bytes 4240 - 427f + 0x91, 0x82, 0x9B, 0xF0, 0x91, 0x82, 0xBA, 0x09, + 0x08, 0xF0, 0x91, 0x82, 0xA5, 0xF0, 0x91, 0x82, + 0xBA, 0x09, 0x42, 0xC2, 0xB4, 0x01, 0x43, 0x20, + 0xCC, 0x81, 0xC9, 0x43, 0x20, 0xCC, 0x83, 0xC9, + 0x43, 0x20, 0xCC, 0x84, 0xC9, 0x43, 0x20, 0xCC, + 0x85, 0xC9, 0x43, 0x20, 0xCC, 0x86, 0xC9, 0x43, + 0x20, 0xCC, 0x87, 0xC9, 0x43, 0x20, 0xCC, 0x88, + 0xC9, 0x43, 0x20, 0xCC, 0x8A, 0xC9, 0x43, 0x20, + // Bytes 4280 - 42bf + 0xCC, 0x8B, 0xC9, 0x43, 0x20, 0xCC, 0x93, 0xC9, + 0x43, 0x20, 0xCC, 0x94, 0xC9, 0x43, 0x20, 0xCC, + 0xA7, 0xA5, 0x43, 0x20, 0xCC, 0xA8, 0xA5, 0x43, + 0x20, 0xCC, 0xB3, 0xB5, 0x43, 0x20, 0xCD, 0x82, + 0xC9, 0x43, 0x20, 0xCD, 0x85, 0xD9, 0x43, 0x20, + 0xD9, 0x8B, 0x59, 0x43, 0x20, 0xD9, 0x8C, 0x5D, + 0x43, 0x20, 0xD9, 0x8D, 0x61, 0x43, 0x20, 0xD9, + 0x8E, 0x65, 0x43, 0x20, 0xD9, 0x8F, 0x69, 0x43, + // Bytes 42c0 - 42ff + 0x20, 0xD9, 0x90, 0x6D, 0x43, 0x20, 0xD9, 0x91, + 0x71, 0x43, 0x20, 0xD9, 0x92, 0x75, 0x43, 0x41, + 0xCC, 0x8A, 0xC9, 0x43, 0x73, 0xCC, 0x87, 0xC9, + 0x43, 0xE1, 0x85, 0xA1, 0x01, 0x43, 0xE1, 0x85, + 0xA2, 0x01, 0x43, 0xE1, 0x85, 0xA3, 0x01, 0x43, + 0xE1, 0x85, 0xA4, 0x01, 0x43, 0xE1, 0x85, 0xA5, + 0x01, 0x43, 0xE1, 0x85, 0xA6, 0x01, 0x43, 0xE1, + 0x85, 0xA7, 0x01, 0x43, 0xE1, 0x85, 0xA8, 0x01, + // Bytes 4300 - 433f + 0x43, 0xE1, 0x85, 0xA9, 0x01, 0x43, 0xE1, 0x85, + 0xAA, 0x01, 0x43, 0xE1, 0x85, 0xAB, 0x01, 0x43, + 0xE1, 0x85, 0xAC, 0x01, 0x43, 0xE1, 0x85, 0xAD, + 0x01, 0x43, 0xE1, 0x85, 0xAE, 0x01, 0x43, 0xE1, + 0x85, 0xAF, 0x01, 0x43, 0xE1, 0x85, 0xB0, 0x01, + 0x43, 0xE1, 0x85, 0xB1, 0x01, 0x43, 0xE1, 0x85, + 0xB2, 0x01, 0x43, 0xE1, 0x85, 0xB3, 0x01, 0x43, + 0xE1, 0x85, 0xB4, 0x01, 0x43, 0xE1, 0x85, 0xB5, + // Bytes 4340 - 437f + 0x01, 0x43, 0xE1, 0x86, 0xAA, 0x01, 0x43, 0xE1, + 0x86, 0xAC, 0x01, 0x43, 0xE1, 0x86, 0xAD, 0x01, + 0x43, 0xE1, 0x86, 0xB0, 0x01, 0x43, 0xE1, 0x86, + 0xB1, 0x01, 0x43, 0xE1, 0x86, 0xB2, 0x01, 0x43, + 0xE1, 0x86, 0xB3, 0x01, 0x43, 0xE1, 0x86, 0xB4, + 0x01, 0x43, 0xE1, 0x86, 0xB5, 0x01, 0x44, 0x20, + 0xE3, 0x82, 0x99, 0x0D, 0x44, 0x20, 0xE3, 0x82, + 0x9A, 0x0D, 0x44, 0xC2, 0xA8, 0xCC, 0x81, 0xCA, + // Bytes 4380 - 43bf + 0x44, 0xCE, 0x91, 0xCC, 0x81, 0xC9, 0x44, 0xCE, + 0x95, 0xCC, 0x81, 0xC9, 0x44, 0xCE, 0x97, 0xCC, + 0x81, 0xC9, 0x44, 0xCE, 0x99, 0xCC, 0x81, 0xC9, + 0x44, 0xCE, 0x9F, 0xCC, 0x81, 0xC9, 0x44, 0xCE, + 0xA5, 0xCC, 0x81, 0xC9, 0x44, 0xCE, 0xA5, 0xCC, + 0x88, 0xC9, 0x44, 0xCE, 0xA9, 0xCC, 0x81, 0xC9, + 0x44, 0xCE, 0xB1, 0xCC, 0x81, 0xC9, 0x44, 0xCE, + 0xB5, 0xCC, 0x81, 0xC9, 0x44, 0xCE, 0xB7, 0xCC, + // Bytes 43c0 - 43ff + 0x81, 0xC9, 0x44, 0xCE, 0xB9, 0xCC, 0x81, 0xC9, + 0x44, 0xCE, 0xBF, 0xCC, 0x81, 0xC9, 0x44, 0xCF, + 0x85, 0xCC, 0x81, 0xC9, 0x44, 0xCF, 0x89, 0xCC, + 0x81, 0xC9, 0x44, 0xD7, 0x90, 0xD6, 0xB7, 0x31, + 0x44, 0xD7, 0x90, 0xD6, 0xB8, 0x35, 0x44, 0xD7, + 0x90, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0x91, 0xD6, + 0xBC, 0x41, 0x44, 0xD7, 0x91, 0xD6, 0xBF, 0x49, + 0x44, 0xD7, 0x92, 0xD6, 0xBC, 0x41, 0x44, 0xD7, + // Bytes 4400 - 443f + 0x93, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0x94, 0xD6, + 0xBC, 0x41, 0x44, 0xD7, 0x95, 0xD6, 0xB9, 0x39, + 0x44, 0xD7, 0x95, 0xD6, 0xBC, 0x41, 0x44, 0xD7, + 0x96, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0x98, 0xD6, + 0xBC, 0x41, 0x44, 0xD7, 0x99, 0xD6, 0xB4, 0x25, + 0x44, 0xD7, 0x99, 0xD6, 0xBC, 0x41, 0x44, 0xD7, + 0x9A, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0x9B, 0xD6, + 0xBC, 0x41, 0x44, 0xD7, 0x9B, 0xD6, 0xBF, 0x49, + // Bytes 4440 - 447f + 0x44, 0xD7, 0x9C, 0xD6, 0xBC, 0x41, 0x44, 0xD7, + 0x9E, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0xA0, 0xD6, + 0xBC, 0x41, 0x44, 0xD7, 0xA1, 0xD6, 0xBC, 0x41, + 0x44, 0xD7, 0xA3, 0xD6, 0xBC, 0x41, 0x44, 0xD7, + 0xA4, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0xA4, 0xD6, + 0xBF, 0x49, 0x44, 0xD7, 0xA6, 0xD6, 0xBC, 0x41, + 0x44, 0xD7, 0xA7, 0xD6, 0xBC, 0x41, 0x44, 0xD7, + 0xA8, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0xA9, 0xD6, + // Bytes 4480 - 44bf + 0xBC, 0x41, 0x44, 0xD7, 0xA9, 0xD7, 0x81, 0x4D, + 0x44, 0xD7, 0xA9, 0xD7, 0x82, 0x51, 0x44, 0xD7, + 0xAA, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0xB2, 0xD6, + 0xB7, 0x31, 0x44, 0xD8, 0xA7, 0xD9, 0x8B, 0x59, + 0x44, 0xD8, 0xA7, 0xD9, 0x93, 0xC9, 0x44, 0xD8, + 0xA7, 0xD9, 0x94, 0xC9, 0x44, 0xD8, 0xA7, 0xD9, + 0x95, 0xB5, 0x44, 0xD8, 0xB0, 0xD9, 0xB0, 0x79, + 0x44, 0xD8, 0xB1, 0xD9, 0xB0, 0x79, 0x44, 0xD9, + // Bytes 44c0 - 44ff + 0x80, 0xD9, 0x8B, 0x59, 0x44, 0xD9, 0x80, 0xD9, + 0x8E, 0x65, 0x44, 0xD9, 0x80, 0xD9, 0x8F, 0x69, + 0x44, 0xD9, 0x80, 0xD9, 0x90, 0x6D, 0x44, 0xD9, + 0x80, 0xD9, 0x91, 0x71, 0x44, 0xD9, 0x80, 0xD9, + 0x92, 0x75, 0x44, 0xD9, 0x87, 0xD9, 0xB0, 0x79, + 0x44, 0xD9, 0x88, 0xD9, 0x94, 0xC9, 0x44, 0xD9, + 0x89, 0xD9, 0xB0, 0x79, 0x44, 0xD9, 0x8A, 0xD9, + 0x94, 0xC9, 0x44, 0xDB, 0x92, 0xD9, 0x94, 0xC9, + // Bytes 4500 - 453f + 0x44, 0xDB, 0x95, 0xD9, 0x94, 0xC9, 0x45, 0x20, + 0xCC, 0x88, 0xCC, 0x80, 0xCA, 0x45, 0x20, 0xCC, + 0x88, 0xCC, 0x81, 0xCA, 0x45, 0x20, 0xCC, 0x88, + 0xCD, 0x82, 0xCA, 0x45, 0x20, 0xCC, 0x93, 0xCC, + 0x80, 0xCA, 0x45, 0x20, 0xCC, 0x93, 0xCC, 0x81, + 0xCA, 0x45, 0x20, 0xCC, 0x93, 0xCD, 0x82, 0xCA, + 0x45, 0x20, 0xCC, 0x94, 0xCC, 0x80, 0xCA, 0x45, + 0x20, 0xCC, 0x94, 0xCC, 0x81, 0xCA, 0x45, 0x20, + // Bytes 4540 - 457f + 0xCC, 0x94, 0xCD, 0x82, 0xCA, 0x45, 0x20, 0xD9, + 0x8C, 0xD9, 0x91, 0x72, 0x45, 0x20, 0xD9, 0x8D, + 0xD9, 0x91, 0x72, 0x45, 0x20, 0xD9, 0x8E, 0xD9, + 0x91, 0x72, 0x45, 0x20, 0xD9, 0x8F, 0xD9, 0x91, + 0x72, 0x45, 0x20, 0xD9, 0x90, 0xD9, 0x91, 0x72, + 0x45, 0x20, 0xD9, 0x91, 0xD9, 0xB0, 0x7A, 0x45, + 0xE2, 0xAB, 0x9D, 0xCC, 0xB8, 0x05, 0x46, 0xCE, + 0xB9, 0xCC, 0x88, 0xCC, 0x81, 0xCA, 0x46, 0xCF, + // Bytes 4580 - 45bf + 0x85, 0xCC, 0x88, 0xCC, 0x81, 0xCA, 0x46, 0xD7, + 0xA9, 0xD6, 0xBC, 0xD7, 0x81, 0x4E, 0x46, 0xD7, + 0xA9, 0xD6, 0xBC, 0xD7, 0x82, 0x52, 0x46, 0xD9, + 0x80, 0xD9, 0x8E, 0xD9, 0x91, 0x72, 0x46, 0xD9, + 0x80, 0xD9, 0x8F, 0xD9, 0x91, 0x72, 0x46, 0xD9, + 0x80, 0xD9, 0x90, 0xD9, 0x91, 0x72, 0x46, 0xE0, + 0xA4, 0x95, 0xE0, 0xA4, 0xBC, 0x09, 0x46, 0xE0, + 0xA4, 0x96, 0xE0, 0xA4, 0xBC, 0x09, 0x46, 0xE0, + // Bytes 45c0 - 45ff + 0xA4, 0x97, 0xE0, 0xA4, 0xBC, 0x09, 0x46, 0xE0, + 0xA4, 0x9C, 0xE0, 0xA4, 0xBC, 0x09, 0x46, 0xE0, + 0xA4, 0xA1, 0xE0, 0xA4, 0xBC, 0x09, 0x46, 0xE0, + 0xA4, 0xA2, 0xE0, 0xA4, 0xBC, 0x09, 0x46, 0xE0, + 0xA4, 0xAB, 0xE0, 0xA4, 0xBC, 0x09, 0x46, 0xE0, + 0xA4, 0xAF, 0xE0, 0xA4, 0xBC, 0x09, 0x46, 0xE0, + 0xA6, 0xA1, 0xE0, 0xA6, 0xBC, 0x09, 0x46, 0xE0, + 0xA6, 0xA2, 0xE0, 0xA6, 0xBC, 0x09, 0x46, 0xE0, + // Bytes 4600 - 463f + 0xA6, 0xAF, 0xE0, 0xA6, 0xBC, 0x09, 0x46, 0xE0, + 0xA8, 0x96, 0xE0, 0xA8, 0xBC, 0x09, 0x46, 0xE0, + 0xA8, 0x97, 0xE0, 0xA8, 0xBC, 0x09, 0x46, 0xE0, + 0xA8, 0x9C, 0xE0, 0xA8, 0xBC, 0x09, 0x46, 0xE0, + 0xA8, 0xAB, 0xE0, 0xA8, 0xBC, 0x09, 0x46, 0xE0, + 0xA8, 0xB2, 0xE0, 0xA8, 0xBC, 0x09, 0x46, 0xE0, + 0xA8, 0xB8, 0xE0, 0xA8, 0xBC, 0x09, 0x46, 0xE0, + 0xAC, 0xA1, 0xE0, 0xAC, 0xBC, 0x09, 0x46, 0xE0, + // Bytes 4640 - 467f + 0xAC, 0xA2, 0xE0, 0xAC, 0xBC, 0x09, 0x46, 0xE0, + 0xBE, 0xB2, 0xE0, 0xBE, 0x80, 0x9D, 0x46, 0xE0, + 0xBE, 0xB3, 0xE0, 0xBE, 0x80, 0x9D, 0x46, 0xE3, + 0x83, 0x86, 0xE3, 0x82, 0x99, 0x0D, 0x48, 0xF0, + 0x9D, 0x85, 0x97, 0xF0, 0x9D, 0x85, 0xA5, 0xAD, + 0x48, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, + 0xA5, 0xAD, 0x48, 0xF0, 0x9D, 0x86, 0xB9, 0xF0, + 0x9D, 0x85, 0xA5, 0xAD, 0x48, 0xF0, 0x9D, 0x86, + // Bytes 4680 - 46bf + 0xBA, 0xF0, 0x9D, 0x85, 0xA5, 0xAD, 0x49, 0xE0, + 0xBE, 0xB2, 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, + 0x9E, 0x49, 0xE0, 0xBE, 0xB3, 0xE0, 0xBD, 0xB1, + 0xE0, 0xBE, 0x80, 0x9E, 0x4C, 0xF0, 0x9D, 0x85, + 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, + 0xAE, 0xAE, 0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0, + 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAF, 0xAE, + 0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, + // Bytes 46c0 - 46ff + 0xA5, 0xF0, 0x9D, 0x85, 0xB0, 0xAE, 0x4C, 0xF0, + 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, + 0x9D, 0x85, 0xB1, 0xAE, 0x4C, 0xF0, 0x9D, 0x85, + 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, + 0xB2, 0xAE, 0x4C, 0xF0, 0x9D, 0x86, 0xB9, 0xF0, + 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAE, 0xAE, + 0x4C, 0xF0, 0x9D, 0x86, 0xB9, 0xF0, 0x9D, 0x85, + 0xA5, 0xF0, 0x9D, 0x85, 0xAF, 0xAE, 0x4C, 0xF0, + // Bytes 4700 - 473f + 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, + 0x9D, 0x85, 0xAE, 0xAE, 0x4C, 0xF0, 0x9D, 0x86, + 0xBA, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, + 0xAF, 0xAE, 0x83, 0x41, 0xCC, 0x82, 0xC9, 0x83, + 0x41, 0xCC, 0x86, 0xC9, 0x83, 0x41, 0xCC, 0x87, + 0xC9, 0x83, 0x41, 0xCC, 0x88, 0xC9, 0x83, 0x41, + 0xCC, 0x8A, 0xC9, 0x83, 0x41, 0xCC, 0xA3, 0xB5, + 0x83, 0x43, 0xCC, 0xA7, 0xA5, 0x83, 0x45, 0xCC, + // Bytes 4740 - 477f + 0x82, 0xC9, 0x83, 0x45, 0xCC, 0x84, 0xC9, 0x83, + 0x45, 0xCC, 0xA3, 0xB5, 0x83, 0x45, 0xCC, 0xA7, + 0xA5, 0x83, 0x49, 0xCC, 0x88, 0xC9, 0x83, 0x4C, + 0xCC, 0xA3, 0xB5, 0x83, 0x4F, 0xCC, 0x82, 0xC9, + 0x83, 0x4F, 0xCC, 0x83, 0xC9, 0x83, 0x4F, 0xCC, + 0x84, 0xC9, 0x83, 0x4F, 0xCC, 0x87, 0xC9, 0x83, + 0x4F, 0xCC, 0x88, 0xC9, 0x83, 0x4F, 0xCC, 0x9B, + 0xAD, 0x83, 0x4F, 0xCC, 0xA3, 0xB5, 0x83, 0x4F, + // Bytes 4780 - 47bf + 0xCC, 0xA8, 0xA5, 0x83, 0x52, 0xCC, 0xA3, 0xB5, + 0x83, 0x53, 0xCC, 0x81, 0xC9, 0x83, 0x53, 0xCC, + 0x8C, 0xC9, 0x83, 0x53, 0xCC, 0xA3, 0xB5, 0x83, + 0x55, 0xCC, 0x83, 0xC9, 0x83, 0x55, 0xCC, 0x84, + 0xC9, 0x83, 0x55, 0xCC, 0x88, 0xC9, 0x83, 0x55, + 0xCC, 0x9B, 0xAD, 0x83, 0x61, 0xCC, 0x82, 0xC9, + 0x83, 0x61, 0xCC, 0x86, 0xC9, 0x83, 0x61, 0xCC, + 0x87, 0xC9, 0x83, 0x61, 0xCC, 0x88, 0xC9, 0x83, + // Bytes 47c0 - 47ff + 0x61, 0xCC, 0x8A, 0xC9, 0x83, 0x61, 0xCC, 0xA3, + 0xB5, 0x83, 0x63, 0xCC, 0xA7, 0xA5, 0x83, 0x65, + 0xCC, 0x82, 0xC9, 0x83, 0x65, 0xCC, 0x84, 0xC9, + 0x83, 0x65, 0xCC, 0xA3, 0xB5, 0x83, 0x65, 0xCC, + 0xA7, 0xA5, 0x83, 0x69, 0xCC, 0x88, 0xC9, 0x83, + 0x6C, 0xCC, 0xA3, 0xB5, 0x83, 0x6F, 0xCC, 0x82, + 0xC9, 0x83, 0x6F, 0xCC, 0x83, 0xC9, 0x83, 0x6F, + 0xCC, 0x84, 0xC9, 0x83, 0x6F, 0xCC, 0x87, 0xC9, + // Bytes 4800 - 483f + 0x83, 0x6F, 0xCC, 0x88, 0xC9, 0x83, 0x6F, 0xCC, + 0x9B, 0xAD, 0x83, 0x6F, 0xCC, 0xA3, 0xB5, 0x83, + 0x6F, 0xCC, 0xA8, 0xA5, 0x83, 0x72, 0xCC, 0xA3, + 0xB5, 0x83, 0x73, 0xCC, 0x81, 0xC9, 0x83, 0x73, + 0xCC, 0x8C, 0xC9, 0x83, 0x73, 0xCC, 0xA3, 0xB5, + 0x83, 0x75, 0xCC, 0x83, 0xC9, 0x83, 0x75, 0xCC, + 0x84, 0xC9, 0x83, 0x75, 0xCC, 0x88, 0xC9, 0x83, + 0x75, 0xCC, 0x9B, 0xAD, 0x84, 0xCE, 0x91, 0xCC, + // Bytes 4840 - 487f + 0x93, 0xC9, 0x84, 0xCE, 0x91, 0xCC, 0x94, 0xC9, + 0x84, 0xCE, 0x95, 0xCC, 0x93, 0xC9, 0x84, 0xCE, + 0x95, 0xCC, 0x94, 0xC9, 0x84, 0xCE, 0x97, 0xCC, + 0x93, 0xC9, 0x84, 0xCE, 0x97, 0xCC, 0x94, 0xC9, + 0x84, 0xCE, 0x99, 0xCC, 0x93, 0xC9, 0x84, 0xCE, + 0x99, 0xCC, 0x94, 0xC9, 0x84, 0xCE, 0x9F, 0xCC, + 0x93, 0xC9, 0x84, 0xCE, 0x9F, 0xCC, 0x94, 0xC9, + 0x84, 0xCE, 0xA5, 0xCC, 0x94, 0xC9, 0x84, 0xCE, + // Bytes 4880 - 48bf + 0xA9, 0xCC, 0x93, 0xC9, 0x84, 0xCE, 0xA9, 0xCC, + 0x94, 0xC9, 0x84, 0xCE, 0xB1, 0xCC, 0x80, 0xC9, + 0x84, 0xCE, 0xB1, 0xCC, 0x81, 0xC9, 0x84, 0xCE, + 0xB1, 0xCC, 0x93, 0xC9, 0x84, 0xCE, 0xB1, 0xCC, + 0x94, 0xC9, 0x84, 0xCE, 0xB1, 0xCD, 0x82, 0xC9, + 0x84, 0xCE, 0xB5, 0xCC, 0x93, 0xC9, 0x84, 0xCE, + 0xB5, 0xCC, 0x94, 0xC9, 0x84, 0xCE, 0xB7, 0xCC, + 0x80, 0xC9, 0x84, 0xCE, 0xB7, 0xCC, 0x81, 0xC9, + // Bytes 48c0 - 48ff + 0x84, 0xCE, 0xB7, 0xCC, 0x93, 0xC9, 0x84, 0xCE, + 0xB7, 0xCC, 0x94, 0xC9, 0x84, 0xCE, 0xB7, 0xCD, + 0x82, 0xC9, 0x84, 0xCE, 0xB9, 0xCC, 0x88, 0xC9, + 0x84, 0xCE, 0xB9, 0xCC, 0x93, 0xC9, 0x84, 0xCE, + 0xB9, 0xCC, 0x94, 0xC9, 0x84, 0xCE, 0xBF, 0xCC, + 0x93, 0xC9, 0x84, 0xCE, 0xBF, 0xCC, 0x94, 0xC9, + 0x84, 0xCF, 0x85, 0xCC, 0x88, 0xC9, 0x84, 0xCF, + 0x85, 0xCC, 0x93, 0xC9, 0x84, 0xCF, 0x85, 0xCC, + // Bytes 4900 - 493f + 0x94, 0xC9, 0x84, 0xCF, 0x89, 0xCC, 0x80, 0xC9, + 0x84, 0xCF, 0x89, 0xCC, 0x81, 0xC9, 0x84, 0xCF, + 0x89, 0xCC, 0x93, 0xC9, 0x84, 0xCF, 0x89, 0xCC, + 0x94, 0xC9, 0x84, 0xCF, 0x89, 0xCD, 0x82, 0xC9, + 0x86, 0xCE, 0x91, 0xCC, 0x93, 0xCC, 0x80, 0xCA, + 0x86, 0xCE, 0x91, 0xCC, 0x93, 0xCC, 0x81, 0xCA, + 0x86, 0xCE, 0x91, 0xCC, 0x93, 0xCD, 0x82, 0xCA, + 0x86, 0xCE, 0x91, 0xCC, 0x94, 0xCC, 0x80, 0xCA, + // Bytes 4940 - 497f + 0x86, 0xCE, 0x91, 0xCC, 0x94, 0xCC, 0x81, 0xCA, + 0x86, 0xCE, 0x91, 0xCC, 0x94, 0xCD, 0x82, 0xCA, + 0x86, 0xCE, 0x97, 0xCC, 0x93, 0xCC, 0x80, 0xCA, + 0x86, 0xCE, 0x97, 0xCC, 0x93, 0xCC, 0x81, 0xCA, + 0x86, 0xCE, 0x97, 0xCC, 0x93, 0xCD, 0x82, 0xCA, + 0x86, 0xCE, 0x97, 0xCC, 0x94, 0xCC, 0x80, 0xCA, + 0x86, 0xCE, 0x97, 0xCC, 0x94, 0xCC, 0x81, 0xCA, + 0x86, 0xCE, 0x97, 0xCC, 0x94, 0xCD, 0x82, 0xCA, + // Bytes 4980 - 49bf + 0x86, 0xCE, 0xA9, 0xCC, 0x93, 0xCC, 0x80, 0xCA, + 0x86, 0xCE, 0xA9, 0xCC, 0x93, 0xCC, 0x81, 0xCA, + 0x86, 0xCE, 0xA9, 0xCC, 0x93, 0xCD, 0x82, 0xCA, + 0x86, 0xCE, 0xA9, 0xCC, 0x94, 0xCC, 0x80, 0xCA, + 0x86, 0xCE, 0xA9, 0xCC, 0x94, 0xCC, 0x81, 0xCA, + 0x86, 0xCE, 0xA9, 0xCC, 0x94, 0xCD, 0x82, 0xCA, + 0x86, 0xCE, 0xB1, 0xCC, 0x93, 0xCC, 0x80, 0xCA, + 0x86, 0xCE, 0xB1, 0xCC, 0x93, 0xCC, 0x81, 0xCA, + // Bytes 49c0 - 49ff + 0x86, 0xCE, 0xB1, 0xCC, 0x93, 0xCD, 0x82, 0xCA, + 0x86, 0xCE, 0xB1, 0xCC, 0x94, 0xCC, 0x80, 0xCA, + 0x86, 0xCE, 0xB1, 0xCC, 0x94, 0xCC, 0x81, 0xCA, + 0x86, 0xCE, 0xB1, 0xCC, 0x94, 0xCD, 0x82, 0xCA, + 0x86, 0xCE, 0xB7, 0xCC, 0x93, 0xCC, 0x80, 0xCA, + 0x86, 0xCE, 0xB7, 0xCC, 0x93, 0xCC, 0x81, 0xCA, + 0x86, 0xCE, 0xB7, 0xCC, 0x93, 0xCD, 0x82, 0xCA, + 0x86, 0xCE, 0xB7, 0xCC, 0x94, 0xCC, 0x80, 0xCA, + // Bytes 4a00 - 4a3f + 0x86, 0xCE, 0xB7, 0xCC, 0x94, 0xCC, 0x81, 0xCA, + 0x86, 0xCE, 0xB7, 0xCC, 0x94, 0xCD, 0x82, 0xCA, + 0x86, 0xCF, 0x89, 0xCC, 0x93, 0xCC, 0x80, 0xCA, + 0x86, 0xCF, 0x89, 0xCC, 0x93, 0xCC, 0x81, 0xCA, + 0x86, 0xCF, 0x89, 0xCC, 0x93, 0xCD, 0x82, 0xCA, + 0x86, 0xCF, 0x89, 0xCC, 0x94, 0xCC, 0x80, 0xCA, + 0x86, 0xCF, 0x89, 0xCC, 0x94, 0xCC, 0x81, 0xCA, + 0x86, 0xCF, 0x89, 0xCC, 0x94, 0xCD, 0x82, 0xCA, + // Bytes 4a40 - 4a7f + 0x42, 0xCC, 0x80, 0xC9, 0x32, 0x42, 0xCC, 0x81, + 0xC9, 0x32, 0x42, 0xCC, 0x93, 0xC9, 0x32, 0x44, + 0xCC, 0x88, 0xCC, 0x81, 0xCA, 0x32, 0x43, 0xE3, + 0x82, 0x99, 0x0D, 0x03, 0x43, 0xE3, 0x82, 0x9A, + 0x0D, 0x03, 0x46, 0xE0, 0xBD, 0xB1, 0xE0, 0xBD, + 0xB2, 0x9E, 0x26, 0x46, 0xE0, 0xBD, 0xB1, 0xE0, + 0xBD, 0xB4, 0xA2, 0x26, 0x46, 0xE0, 0xBD, 0xB1, + 0xE0, 0xBE, 0x80, 0x9E, 0x26, 0x00, 0x01, +} + +// lookup returns the trie value for the first UTF-8 encoding in s and +// the width in bytes of this encoding. The size will be 0 if s does not +// hold enough bytes to complete the encoding. len(s) must be greater than 0. +func (t *nfcTrie) lookup(s []byte) (v uint16, sz int) { + c0 := s[0] + switch { + case c0 < 0x80: // is ASCII + return nfcValues[c0], 1 + case c0 < 0xC0: + return 0, 1 // Illegal UTF-8: not a starter, not ASCII. + case c0 < 0xE0: // 2-byte UTF-8 + if len(s) < 2 { + return 0, 0 + } + i := nfcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c1), 2 + case c0 < 0xF0: // 3-byte UTF-8 + if len(s) < 3 { + return 0, 0 + } + i := nfcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = nfcIndex[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c2), 3 + case c0 < 0xF8: // 4-byte UTF-8 + if len(s) < 4 { + return 0, 0 + } + i := nfcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = nfcIndex[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + o = uint32(i)<<6 + uint32(c2) + i = nfcIndex[o] + c3 := s[3] + if c3 < 0x80 || 0xC0 <= c3 { + return 0, 3 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c3), 4 + } + // Illegal rune + return 0, 1 +} + +// lookupUnsafe returns the trie value for the first UTF-8 encoding in s. +// s must start with a full and valid UTF-8 encoded rune. +func (t *nfcTrie) lookupUnsafe(s []byte) uint16 { + c0 := s[0] + if c0 < 0x80 { // is ASCII + return nfcValues[c0] + } + i := nfcIndex[c0] + if c0 < 0xE0 { // 2-byte UTF-8 + return t.lookupValue(uint32(i), s[1]) + } + i = nfcIndex[uint32(i)<<6+uint32(s[1])] + if c0 < 0xF0 { // 3-byte UTF-8 + return t.lookupValue(uint32(i), s[2]) + } + i = nfcIndex[uint32(i)<<6+uint32(s[2])] + if c0 < 0xF8 { // 4-byte UTF-8 + return t.lookupValue(uint32(i), s[3]) + } + return 0 +} + +// lookupString returns the trie value for the first UTF-8 encoding in s and +// the width in bytes of this encoding. The size will be 0 if s does not +// hold enough bytes to complete the encoding. len(s) must be greater than 0. +func (t *nfcTrie) lookupString(s string) (v uint16, sz int) { + c0 := s[0] + switch { + case c0 < 0x80: // is ASCII + return nfcValues[c0], 1 + case c0 < 0xC0: + return 0, 1 // Illegal UTF-8: not a starter, not ASCII. + case c0 < 0xE0: // 2-byte UTF-8 + if len(s) < 2 { + return 0, 0 + } + i := nfcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c1), 2 + case c0 < 0xF0: // 3-byte UTF-8 + if len(s) < 3 { + return 0, 0 + } + i := nfcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = nfcIndex[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c2), 3 + case c0 < 0xF8: // 4-byte UTF-8 + if len(s) < 4 { + return 0, 0 + } + i := nfcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = nfcIndex[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + o = uint32(i)<<6 + uint32(c2) + i = nfcIndex[o] + c3 := s[3] + if c3 < 0x80 || 0xC0 <= c3 { + return 0, 3 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c3), 4 + } + // Illegal rune + return 0, 1 +} + +// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s. +// s must start with a full and valid UTF-8 encoded rune. +func (t *nfcTrie) lookupStringUnsafe(s string) uint16 { + c0 := s[0] + if c0 < 0x80 { // is ASCII + return nfcValues[c0] + } + i := nfcIndex[c0] + if c0 < 0xE0 { // 2-byte UTF-8 + return t.lookupValue(uint32(i), s[1]) + } + i = nfcIndex[uint32(i)<<6+uint32(s[1])] + if c0 < 0xF0 { // 3-byte UTF-8 + return t.lookupValue(uint32(i), s[2]) + } + i = nfcIndex[uint32(i)<<6+uint32(s[2])] + if c0 < 0xF8 { // 4-byte UTF-8 + return t.lookupValue(uint32(i), s[3]) + } + return 0 +} + +// nfcTrie. Total size: 10270 bytes (10.03 KiB). Checksum: d7e415c88f2e510a. +type nfcTrie struct{} + +func newNfcTrie(i int) *nfcTrie { + return &nfcTrie{} +} + +// lookupValue determines the type of block n and looks up the value for b. +func (t *nfcTrie) lookupValue(n uint32, b byte) uint16 { + switch { + case n < 44: + return uint16(nfcValues[n<<6+uint32(b)]) + default: + n -= 44 + return uint16(nfcSparse.lookup(n, b)) + } +} + +// nfcValues: 46 blocks, 2944 entries, 5888 bytes +// The third block is the zero block. +var nfcValues = [2944]uint16{ + // Block 0x0, offset 0x0 + 0x3c: 0xa000, 0x3d: 0xa000, 0x3e: 0xa000, + // Block 0x1, offset 0x40 + 0x41: 0xa000, 0x42: 0xa000, 0x43: 0xa000, 0x44: 0xa000, 0x45: 0xa000, + 0x46: 0xa000, 0x47: 0xa000, 0x48: 0xa000, 0x49: 0xa000, 0x4a: 0xa000, 0x4b: 0xa000, + 0x4c: 0xa000, 0x4d: 0xa000, 0x4e: 0xa000, 0x4f: 0xa000, 0x50: 0xa000, + 0x52: 0xa000, 0x53: 0xa000, 0x54: 0xa000, 0x55: 0xa000, 0x56: 0xa000, 0x57: 0xa000, + 0x58: 0xa000, 0x59: 0xa000, 0x5a: 0xa000, + 0x61: 0xa000, 0x62: 0xa000, 0x63: 0xa000, + 0x64: 0xa000, 0x65: 0xa000, 0x66: 0xa000, 0x67: 0xa000, 0x68: 0xa000, 0x69: 0xa000, + 0x6a: 0xa000, 0x6b: 0xa000, 0x6c: 0xa000, 0x6d: 0xa000, 0x6e: 0xa000, 0x6f: 0xa000, + 0x70: 0xa000, 0x72: 0xa000, 0x73: 0xa000, 0x74: 0xa000, 0x75: 0xa000, + 0x76: 0xa000, 0x77: 0xa000, 0x78: 0xa000, 0x79: 0xa000, 0x7a: 0xa000, + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0xc0: 0x2f6b, 0xc1: 0x2f70, 0xc2: 0x471a, 0xc3: 0x2f75, 0xc4: 0x4729, 0xc5: 0x472e, + 0xc6: 0xa000, 0xc7: 0x4738, 0xc8: 0x2fde, 0xc9: 0x2fe3, 0xca: 0x473d, 0xcb: 0x2ff7, + 0xcc: 0x306a, 0xcd: 0x306f, 0xce: 0x3074, 0xcf: 0x4751, 0xd1: 0x3100, + 0xd2: 0x3123, 0xd3: 0x3128, 0xd4: 0x475b, 0xd5: 0x4760, 0xd6: 0x476f, + 0xd8: 0xa000, 0xd9: 0x31af, 0xda: 0x31b4, 0xdb: 0x31b9, 0xdc: 0x47a1, 0xdd: 0x3231, + 0xe0: 0x3277, 0xe1: 0x327c, 0xe2: 0x47ab, 0xe3: 0x3281, + 0xe4: 0x47ba, 0xe5: 0x47bf, 0xe6: 0xa000, 0xe7: 0x47c9, 0xe8: 0x32ea, 0xe9: 0x32ef, + 0xea: 0x47ce, 0xeb: 0x3303, 0xec: 0x337b, 0xed: 0x3380, 0xee: 0x3385, 0xef: 0x47e2, + 0xf1: 0x3411, 0xf2: 0x3434, 0xf3: 0x3439, 0xf4: 0x47ec, 0xf5: 0x47f1, + 0xf6: 0x4800, 0xf8: 0xa000, 0xf9: 0x34c5, 0xfa: 0x34ca, 0xfb: 0x34cf, + 0xfc: 0x4832, 0xfd: 0x354c, 0xff: 0x3565, + // Block 0x4, offset 0x100 + 0x100: 0x2f7a, 0x101: 0x3286, 0x102: 0x471f, 0x103: 0x47b0, 0x104: 0x2f98, 0x105: 0x32a4, + 0x106: 0x2fac, 0x107: 0x32b8, 0x108: 0x2fb1, 0x109: 0x32bd, 0x10a: 0x2fb6, 0x10b: 0x32c2, + 0x10c: 0x2fbb, 0x10d: 0x32c7, 0x10e: 0x2fc5, 0x10f: 0x32d1, + 0x112: 0x4742, 0x113: 0x47d3, 0x114: 0x2fed, 0x115: 0x32f9, 0x116: 0x2ff2, 0x117: 0x32fe, + 0x118: 0x3010, 0x119: 0x331c, 0x11a: 0x3001, 0x11b: 0x330d, 0x11c: 0x3029, 0x11d: 0x3335, + 0x11e: 0x3033, 0x11f: 0x333f, 0x120: 0x3038, 0x121: 0x3344, 0x122: 0x3042, 0x123: 0x334e, + 0x124: 0x3047, 0x125: 0x3353, 0x128: 0x3079, 0x129: 0x338a, + 0x12a: 0x307e, 0x12b: 0x338f, 0x12c: 0x3083, 0x12d: 0x3394, 0x12e: 0x30a6, 0x12f: 0x33b2, + 0x130: 0x3088, 0x134: 0x30b0, 0x135: 0x33bc, + 0x136: 0x30c4, 0x137: 0x33d5, 0x139: 0x30ce, 0x13a: 0x33df, 0x13b: 0x30d8, + 0x13c: 0x33e9, 0x13d: 0x30d3, 0x13e: 0x33e4, + // Block 0x5, offset 0x140 + 0x143: 0x30fb, 0x144: 0x340c, 0x145: 0x3114, + 0x146: 0x3425, 0x147: 0x310a, 0x148: 0x341b, + 0x14c: 0x4765, 0x14d: 0x47f6, 0x14e: 0x312d, 0x14f: 0x343e, 0x150: 0x3137, 0x151: 0x3448, + 0x154: 0x3155, 0x155: 0x3466, 0x156: 0x316e, 0x157: 0x347f, + 0x158: 0x315f, 0x159: 0x3470, 0x15a: 0x4788, 0x15b: 0x4819, 0x15c: 0x3178, 0x15d: 0x3489, + 0x15e: 0x3187, 0x15f: 0x3498, 0x160: 0x478d, 0x161: 0x481e, 0x162: 0x31a0, 0x163: 0x34b6, + 0x164: 0x3191, 0x165: 0x34a7, 0x168: 0x4797, 0x169: 0x4828, + 0x16a: 0x479c, 0x16b: 0x482d, 0x16c: 0x31be, 0x16d: 0x34d4, 0x16e: 0x31c8, 0x16f: 0x34de, + 0x170: 0x31cd, 0x171: 0x34e3, 0x172: 0x31eb, 0x173: 0x3501, 0x174: 0x320e, 0x175: 0x3524, + 0x176: 0x3236, 0x177: 0x3551, 0x178: 0x324a, 0x179: 0x3259, 0x17a: 0x3579, 0x17b: 0x3263, + 0x17c: 0x3583, 0x17d: 0x3268, 0x17e: 0x3588, 0x17f: 0xa000, + // Block 0x6, offset 0x180 + 0x184: 0x8100, 0x185: 0x8100, + 0x186: 0x8100, + 0x18d: 0x2f84, 0x18e: 0x3290, 0x18f: 0x3092, 0x190: 0x339e, 0x191: 0x313c, + 0x192: 0x344d, 0x193: 0x31d2, 0x194: 0x34e8, 0x195: 0x39cb, 0x196: 0x3b5a, 0x197: 0x39c4, + 0x198: 0x3b53, 0x199: 0x39d2, 0x19a: 0x3b61, 0x19b: 0x39bd, 0x19c: 0x3b4c, + 0x19e: 0x38ac, 0x19f: 0x3a3b, 0x1a0: 0x38a5, 0x1a1: 0x3a34, 0x1a2: 0x35af, 0x1a3: 0x35c1, + 0x1a6: 0x303d, 0x1a7: 0x3349, 0x1a8: 0x30ba, 0x1a9: 0x33cb, + 0x1aa: 0x477e, 0x1ab: 0x480f, 0x1ac: 0x398c, 0x1ad: 0x3b1b, 0x1ae: 0x35d3, 0x1af: 0x35d9, + 0x1b0: 0x33c1, 0x1b4: 0x3024, 0x1b5: 0x3330, + 0x1b8: 0x30f6, 0x1b9: 0x3407, 0x1ba: 0x38b3, 0x1bb: 0x3a42, + 0x1bc: 0x35a9, 0x1bd: 0x35bb, 0x1be: 0x35b5, 0x1bf: 0x35c7, + // Block 0x7, offset 0x1c0 + 0x1c0: 0x2f89, 0x1c1: 0x3295, 0x1c2: 0x2f8e, 0x1c3: 0x329a, 0x1c4: 0x3006, 0x1c5: 0x3312, + 0x1c6: 0x300b, 0x1c7: 0x3317, 0x1c8: 0x3097, 0x1c9: 0x33a3, 0x1ca: 0x309c, 0x1cb: 0x33a8, + 0x1cc: 0x3141, 0x1cd: 0x3452, 0x1ce: 0x3146, 0x1cf: 0x3457, 0x1d0: 0x3164, 0x1d1: 0x3475, + 0x1d2: 0x3169, 0x1d3: 0x347a, 0x1d4: 0x31d7, 0x1d5: 0x34ed, 0x1d6: 0x31dc, 0x1d7: 0x34f2, + 0x1d8: 0x3182, 0x1d9: 0x3493, 0x1da: 0x319b, 0x1db: 0x34b1, + 0x1de: 0x3056, 0x1df: 0x3362, + 0x1e6: 0x4724, 0x1e7: 0x47b5, 0x1e8: 0x474c, 0x1e9: 0x47dd, + 0x1ea: 0x395b, 0x1eb: 0x3aea, 0x1ec: 0x3938, 0x1ed: 0x3ac7, 0x1ee: 0x476a, 0x1ef: 0x47fb, + 0x1f0: 0x3954, 0x1f1: 0x3ae3, 0x1f2: 0x3240, 0x1f3: 0x355b, + // Block 0x8, offset 0x200 + 0x200: 0x9932, 0x201: 0x9932, 0x202: 0x9932, 0x203: 0x9932, 0x204: 0x9932, 0x205: 0x8132, + 0x206: 0x9932, 0x207: 0x9932, 0x208: 0x9932, 0x209: 0x9932, 0x20a: 0x9932, 0x20b: 0x9932, + 0x20c: 0x9932, 0x20d: 0x8132, 0x20e: 0x8132, 0x20f: 0x9932, 0x210: 0x8132, 0x211: 0x9932, + 0x212: 0x8132, 0x213: 0x9932, 0x214: 0x9932, 0x215: 0x8133, 0x216: 0x812d, 0x217: 0x812d, + 0x218: 0x812d, 0x219: 0x812d, 0x21a: 0x8133, 0x21b: 0x992b, 0x21c: 0x812d, 0x21d: 0x812d, + 0x21e: 0x812d, 0x21f: 0x812d, 0x220: 0x812d, 0x221: 0x8129, 0x222: 0x8129, 0x223: 0x992d, + 0x224: 0x992d, 0x225: 0x992d, 0x226: 0x992d, 0x227: 0x9929, 0x228: 0x9929, 0x229: 0x812d, + 0x22a: 0x812d, 0x22b: 0x812d, 0x22c: 0x812d, 0x22d: 0x992d, 0x22e: 0x992d, 0x22f: 0x812d, + 0x230: 0x992d, 0x231: 0x992d, 0x232: 0x812d, 0x233: 0x812d, 0x234: 0x8101, 0x235: 0x8101, + 0x236: 0x8101, 0x237: 0x8101, 0x238: 0x9901, 0x239: 0x812d, 0x23a: 0x812d, 0x23b: 0x812d, + 0x23c: 0x812d, 0x23d: 0x8132, 0x23e: 0x8132, 0x23f: 0x8132, + // Block 0x9, offset 0x240 + 0x240: 0x4a40, 0x241: 0x4a45, 0x242: 0x9932, 0x243: 0x4a4a, 0x244: 0x4a4f, 0x245: 0x9936, + 0x246: 0x8132, 0x247: 0x812d, 0x248: 0x812d, 0x249: 0x812d, 0x24a: 0x8132, 0x24b: 0x8132, + 0x24c: 0x8132, 0x24d: 0x812d, 0x24e: 0x812d, 0x250: 0x8132, 0x251: 0x8132, + 0x252: 0x8132, 0x253: 0x812d, 0x254: 0x812d, 0x255: 0x812d, 0x256: 0x812d, 0x257: 0x8132, + 0x258: 0x8133, 0x259: 0x812d, 0x25a: 0x812d, 0x25b: 0x8132, 0x25c: 0x8134, 0x25d: 0x8135, + 0x25e: 0x8135, 0x25f: 0x8134, 0x260: 0x8135, 0x261: 0x8135, 0x262: 0x8134, 0x263: 0x8132, + 0x264: 0x8132, 0x265: 0x8132, 0x266: 0x8132, 0x267: 0x8132, 0x268: 0x8132, 0x269: 0x8132, + 0x26a: 0x8132, 0x26b: 0x8132, 0x26c: 0x8132, 0x26d: 0x8132, 0x26e: 0x8132, 0x26f: 0x8132, + 0x274: 0x0170, + 0x27a: 0x8100, + 0x27e: 0x0037, + // Block 0xa, offset 0x280 + 0x284: 0x8100, 0x285: 0x359d, + 0x286: 0x35e5, 0x287: 0x00ce, 0x288: 0x3603, 0x289: 0x360f, 0x28a: 0x3621, + 0x28c: 0x363f, 0x28e: 0x3651, 0x28f: 0x366f, 0x290: 0x3e04, 0x291: 0xa000, + 0x295: 0xa000, 0x297: 0xa000, + 0x299: 0xa000, + 0x29f: 0xa000, 0x2a1: 0xa000, + 0x2a5: 0xa000, 0x2a9: 0xa000, + 0x2aa: 0x3633, 0x2ab: 0x3663, 0x2ac: 0x4890, 0x2ad: 0x3693, 0x2ae: 0x48ba, 0x2af: 0x36a5, + 0x2b0: 0x3e6c, 0x2b1: 0xa000, 0x2b5: 0xa000, + 0x2b7: 0xa000, 0x2b9: 0xa000, + 0x2bf: 0xa000, + // Block 0xb, offset 0x2c0 + 0x2c0: 0x371d, 0x2c1: 0x3729, 0x2c3: 0x3717, + 0x2c6: 0xa000, 0x2c7: 0x3705, + 0x2cc: 0x3759, 0x2cd: 0x3741, 0x2ce: 0x376b, 0x2d0: 0xa000, + 0x2d3: 0xa000, 0x2d5: 0xa000, 0x2d6: 0xa000, 0x2d7: 0xa000, + 0x2d8: 0xa000, 0x2d9: 0x374d, 0x2da: 0xa000, + 0x2de: 0xa000, 0x2e3: 0xa000, + 0x2e7: 0xa000, + 0x2eb: 0xa000, 0x2ed: 0xa000, + 0x2f0: 0xa000, 0x2f3: 0xa000, 0x2f5: 0xa000, + 0x2f6: 0xa000, 0x2f7: 0xa000, 0x2f8: 0xa000, 0x2f9: 0x37d1, 0x2fa: 0xa000, + 0x2fe: 0xa000, + // Block 0xc, offset 0x300 + 0x301: 0x372f, 0x302: 0x37b3, + 0x310: 0x370b, 0x311: 0x378f, + 0x312: 0x3711, 0x313: 0x3795, 0x316: 0x3723, 0x317: 0x37a7, + 0x318: 0xa000, 0x319: 0xa000, 0x31a: 0x3825, 0x31b: 0x382b, 0x31c: 0x3735, 0x31d: 0x37b9, + 0x31e: 0x373b, 0x31f: 0x37bf, 0x322: 0x3747, 0x323: 0x37cb, + 0x324: 0x3753, 0x325: 0x37d7, 0x326: 0x375f, 0x327: 0x37e3, 0x328: 0xa000, 0x329: 0xa000, + 0x32a: 0x3831, 0x32b: 0x3837, 0x32c: 0x3789, 0x32d: 0x380d, 0x32e: 0x3765, 0x32f: 0x37e9, + 0x330: 0x3771, 0x331: 0x37f5, 0x332: 0x3777, 0x333: 0x37fb, 0x334: 0x377d, 0x335: 0x3801, + 0x338: 0x3783, 0x339: 0x3807, + // Block 0xd, offset 0x340 + 0x351: 0x812d, + 0x352: 0x8132, 0x353: 0x8132, 0x354: 0x8132, 0x355: 0x8132, 0x356: 0x812d, 0x357: 0x8132, + 0x358: 0x8132, 0x359: 0x8132, 0x35a: 0x812e, 0x35b: 0x812d, 0x35c: 0x8132, 0x35d: 0x8132, + 0x35e: 0x8132, 0x35f: 0x8132, 0x360: 0x8132, 0x361: 0x8132, 0x362: 0x812d, 0x363: 0x812d, + 0x364: 0x812d, 0x365: 0x812d, 0x366: 0x812d, 0x367: 0x812d, 0x368: 0x8132, 0x369: 0x8132, + 0x36a: 0x812d, 0x36b: 0x8132, 0x36c: 0x8132, 0x36d: 0x812e, 0x36e: 0x8131, 0x36f: 0x8132, + 0x370: 0x8105, 0x371: 0x8106, 0x372: 0x8107, 0x373: 0x8108, 0x374: 0x8109, 0x375: 0x810a, + 0x376: 0x810b, 0x377: 0x810c, 0x378: 0x810d, 0x379: 0x810e, 0x37a: 0x810e, 0x37b: 0x810f, + 0x37c: 0x8110, 0x37d: 0x8111, 0x37f: 0x8112, + // Block 0xe, offset 0x380 + 0x388: 0xa000, 0x38a: 0xa000, 0x38b: 0x8116, + 0x38c: 0x8117, 0x38d: 0x8118, 0x38e: 0x8119, 0x38f: 0x811a, 0x390: 0x811b, 0x391: 0x811c, + 0x392: 0x811d, 0x393: 0x9932, 0x394: 0x9932, 0x395: 0x992d, 0x396: 0x812d, 0x397: 0x8132, + 0x398: 0x8132, 0x399: 0x8132, 0x39a: 0x8132, 0x39b: 0x8132, 0x39c: 0x812d, 0x39d: 0x8132, + 0x39e: 0x8132, 0x39f: 0x812d, + 0x3b0: 0x811e, + // Block 0xf, offset 0x3c0 + 0x3c5: 0xa000, + 0x3c6: 0x2d22, 0x3c7: 0xa000, 0x3c8: 0x2d2a, 0x3c9: 0xa000, 0x3ca: 0x2d32, 0x3cb: 0xa000, + 0x3cc: 0x2d3a, 0x3cd: 0xa000, 0x3ce: 0x2d42, 0x3d1: 0xa000, + 0x3d2: 0x2d4a, + 0x3f4: 0x8102, 0x3f5: 0x9900, + 0x3fa: 0xa000, 0x3fb: 0x2d52, + 0x3fc: 0xa000, 0x3fd: 0x2d5a, 0x3fe: 0xa000, 0x3ff: 0xa000, + // Block 0x10, offset 0x400 + 0x400: 0x2f93, 0x401: 0x329f, 0x402: 0x2f9d, 0x403: 0x32a9, 0x404: 0x2fa2, 0x405: 0x32ae, + 0x406: 0x2fa7, 0x407: 0x32b3, 0x408: 0x38c8, 0x409: 0x3a57, 0x40a: 0x2fc0, 0x40b: 0x32cc, + 0x40c: 0x2fca, 0x40d: 0x32d6, 0x40e: 0x2fd9, 0x40f: 0x32e5, 0x410: 0x2fcf, 0x411: 0x32db, + 0x412: 0x2fd4, 0x413: 0x32e0, 0x414: 0x38eb, 0x415: 0x3a7a, 0x416: 0x38f2, 0x417: 0x3a81, + 0x418: 0x3015, 0x419: 0x3321, 0x41a: 0x301a, 0x41b: 0x3326, 0x41c: 0x3900, 0x41d: 0x3a8f, + 0x41e: 0x301f, 0x41f: 0x332b, 0x420: 0x302e, 0x421: 0x333a, 0x422: 0x304c, 0x423: 0x3358, + 0x424: 0x305b, 0x425: 0x3367, 0x426: 0x3051, 0x427: 0x335d, 0x428: 0x3060, 0x429: 0x336c, + 0x42a: 0x3065, 0x42b: 0x3371, 0x42c: 0x30ab, 0x42d: 0x33b7, 0x42e: 0x3907, 0x42f: 0x3a96, + 0x430: 0x30b5, 0x431: 0x33c6, 0x432: 0x30bf, 0x433: 0x33d0, 0x434: 0x30c9, 0x435: 0x33da, + 0x436: 0x4756, 0x437: 0x47e7, 0x438: 0x390e, 0x439: 0x3a9d, 0x43a: 0x30e2, 0x43b: 0x33f3, + 0x43c: 0x30dd, 0x43d: 0x33ee, 0x43e: 0x30e7, 0x43f: 0x33f8, + // Block 0x11, offset 0x440 + 0x440: 0x30ec, 0x441: 0x33fd, 0x442: 0x30f1, 0x443: 0x3402, 0x444: 0x3105, 0x445: 0x3416, + 0x446: 0x310f, 0x447: 0x3420, 0x448: 0x311e, 0x449: 0x342f, 0x44a: 0x3119, 0x44b: 0x342a, + 0x44c: 0x3931, 0x44d: 0x3ac0, 0x44e: 0x393f, 0x44f: 0x3ace, 0x450: 0x3946, 0x451: 0x3ad5, + 0x452: 0x394d, 0x453: 0x3adc, 0x454: 0x314b, 0x455: 0x345c, 0x456: 0x3150, 0x457: 0x3461, + 0x458: 0x315a, 0x459: 0x346b, 0x45a: 0x4783, 0x45b: 0x4814, 0x45c: 0x3993, 0x45d: 0x3b22, + 0x45e: 0x3173, 0x45f: 0x3484, 0x460: 0x317d, 0x461: 0x348e, 0x462: 0x4792, 0x463: 0x4823, + 0x464: 0x399a, 0x465: 0x3b29, 0x466: 0x39a1, 0x467: 0x3b30, 0x468: 0x39a8, 0x469: 0x3b37, + 0x46a: 0x318c, 0x46b: 0x349d, 0x46c: 0x3196, 0x46d: 0x34ac, 0x46e: 0x31aa, 0x46f: 0x34c0, + 0x470: 0x31a5, 0x471: 0x34bb, 0x472: 0x31e6, 0x473: 0x34fc, 0x474: 0x31f5, 0x475: 0x350b, + 0x476: 0x31f0, 0x477: 0x3506, 0x478: 0x39af, 0x479: 0x3b3e, 0x47a: 0x39b6, 0x47b: 0x3b45, + 0x47c: 0x31fa, 0x47d: 0x3510, 0x47e: 0x31ff, 0x47f: 0x3515, + // Block 0x12, offset 0x480 + 0x480: 0x3204, 0x481: 0x351a, 0x482: 0x3209, 0x483: 0x351f, 0x484: 0x3218, 0x485: 0x352e, + 0x486: 0x3213, 0x487: 0x3529, 0x488: 0x321d, 0x489: 0x3538, 0x48a: 0x3222, 0x48b: 0x353d, + 0x48c: 0x3227, 0x48d: 0x3542, 0x48e: 0x3245, 0x48f: 0x3560, 0x490: 0x325e, 0x491: 0x357e, + 0x492: 0x326d, 0x493: 0x358d, 0x494: 0x3272, 0x495: 0x3592, 0x496: 0x3376, 0x497: 0x34a2, + 0x498: 0x3533, 0x499: 0x356f, 0x49b: 0x35cd, + 0x4a0: 0x4733, 0x4a1: 0x47c4, 0x4a2: 0x2f7f, 0x4a3: 0x328b, + 0x4a4: 0x3874, 0x4a5: 0x3a03, 0x4a6: 0x386d, 0x4a7: 0x39fc, 0x4a8: 0x3882, 0x4a9: 0x3a11, + 0x4aa: 0x387b, 0x4ab: 0x3a0a, 0x4ac: 0x38ba, 0x4ad: 0x3a49, 0x4ae: 0x3890, 0x4af: 0x3a1f, + 0x4b0: 0x3889, 0x4b1: 0x3a18, 0x4b2: 0x389e, 0x4b3: 0x3a2d, 0x4b4: 0x3897, 0x4b5: 0x3a26, + 0x4b6: 0x38c1, 0x4b7: 0x3a50, 0x4b8: 0x4747, 0x4b9: 0x47d8, 0x4ba: 0x2ffc, 0x4bb: 0x3308, + 0x4bc: 0x2fe8, 0x4bd: 0x32f4, 0x4be: 0x38d6, 0x4bf: 0x3a65, + // Block 0x13, offset 0x4c0 + 0x4c0: 0x38cf, 0x4c1: 0x3a5e, 0x4c2: 0x38e4, 0x4c3: 0x3a73, 0x4c4: 0x38dd, 0x4c5: 0x3a6c, + 0x4c6: 0x38f9, 0x4c7: 0x3a88, 0x4c8: 0x308d, 0x4c9: 0x3399, 0x4ca: 0x30a1, 0x4cb: 0x33ad, + 0x4cc: 0x4779, 0x4cd: 0x480a, 0x4ce: 0x3132, 0x4cf: 0x3443, 0x4d0: 0x391c, 0x4d1: 0x3aab, + 0x4d2: 0x3915, 0x4d3: 0x3aa4, 0x4d4: 0x392a, 0x4d5: 0x3ab9, 0x4d6: 0x3923, 0x4d7: 0x3ab2, + 0x4d8: 0x3985, 0x4d9: 0x3b14, 0x4da: 0x3969, 0x4db: 0x3af8, 0x4dc: 0x3962, 0x4dd: 0x3af1, + 0x4de: 0x3977, 0x4df: 0x3b06, 0x4e0: 0x3970, 0x4e1: 0x3aff, 0x4e2: 0x397e, 0x4e3: 0x3b0d, + 0x4e4: 0x31e1, 0x4e5: 0x34f7, 0x4e6: 0x31c3, 0x4e7: 0x34d9, 0x4e8: 0x39e0, 0x4e9: 0x3b6f, + 0x4ea: 0x39d9, 0x4eb: 0x3b68, 0x4ec: 0x39ee, 0x4ed: 0x3b7d, 0x4ee: 0x39e7, 0x4ef: 0x3b76, + 0x4f0: 0x39f5, 0x4f1: 0x3b84, 0x4f2: 0x322c, 0x4f3: 0x3547, 0x4f4: 0x3254, 0x4f5: 0x3574, + 0x4f6: 0x324f, 0x4f7: 0x356a, 0x4f8: 0x323b, 0x4f9: 0x3556, + // Block 0x14, offset 0x500 + 0x500: 0x4896, 0x501: 0x489c, 0x502: 0x49b0, 0x503: 0x49c8, 0x504: 0x49b8, 0x505: 0x49d0, + 0x506: 0x49c0, 0x507: 0x49d8, 0x508: 0x483c, 0x509: 0x4842, 0x50a: 0x4920, 0x50b: 0x4938, + 0x50c: 0x4928, 0x50d: 0x4940, 0x50e: 0x4930, 0x50f: 0x4948, 0x510: 0x48a8, 0x511: 0x48ae, + 0x512: 0x3db4, 0x513: 0x3dc4, 0x514: 0x3dbc, 0x515: 0x3dcc, + 0x518: 0x4848, 0x519: 0x484e, 0x51a: 0x3ce4, 0x51b: 0x3cf4, 0x51c: 0x3cec, 0x51d: 0x3cfc, + 0x520: 0x48c0, 0x521: 0x48c6, 0x522: 0x49e0, 0x523: 0x49f8, + 0x524: 0x49e8, 0x525: 0x4a00, 0x526: 0x49f0, 0x527: 0x4a08, 0x528: 0x4854, 0x529: 0x485a, + 0x52a: 0x4950, 0x52b: 0x4968, 0x52c: 0x4958, 0x52d: 0x4970, 0x52e: 0x4960, 0x52f: 0x4978, + 0x530: 0x48d8, 0x531: 0x48de, 0x532: 0x3e14, 0x533: 0x3e2c, 0x534: 0x3e1c, 0x535: 0x3e34, + 0x536: 0x3e24, 0x537: 0x3e3c, 0x538: 0x4860, 0x539: 0x4866, 0x53a: 0x3d14, 0x53b: 0x3d2c, + 0x53c: 0x3d1c, 0x53d: 0x3d34, 0x53e: 0x3d24, 0x53f: 0x3d3c, + // Block 0x15, offset 0x540 + 0x540: 0x48e4, 0x541: 0x48ea, 0x542: 0x3e44, 0x543: 0x3e54, 0x544: 0x3e4c, 0x545: 0x3e5c, + 0x548: 0x486c, 0x549: 0x4872, 0x54a: 0x3d44, 0x54b: 0x3d54, + 0x54c: 0x3d4c, 0x54d: 0x3d5c, 0x550: 0x48f6, 0x551: 0x48fc, + 0x552: 0x3e7c, 0x553: 0x3e94, 0x554: 0x3e84, 0x555: 0x3e9c, 0x556: 0x3e8c, 0x557: 0x3ea4, + 0x559: 0x4878, 0x55b: 0x3d64, 0x55d: 0x3d6c, + 0x55f: 0x3d74, 0x560: 0x490e, 0x561: 0x4914, 0x562: 0x4a10, 0x563: 0x4a28, + 0x564: 0x4a18, 0x565: 0x4a30, 0x566: 0x4a20, 0x567: 0x4a38, 0x568: 0x487e, 0x569: 0x4884, + 0x56a: 0x4980, 0x56b: 0x4998, 0x56c: 0x4988, 0x56d: 0x49a0, 0x56e: 0x4990, 0x56f: 0x49a8, + 0x570: 0x488a, 0x571: 0x43b0, 0x572: 0x368d, 0x573: 0x43b6, 0x574: 0x48b4, 0x575: 0x43bc, + 0x576: 0x369f, 0x577: 0x43c2, 0x578: 0x36bd, 0x579: 0x43c8, 0x57a: 0x36d5, 0x57b: 0x43ce, + 0x57c: 0x4902, 0x57d: 0x43d4, + // Block 0x16, offset 0x580 + 0x580: 0x3d9c, 0x581: 0x3da4, 0x582: 0x4180, 0x583: 0x419e, 0x584: 0x418a, 0x585: 0x41a8, + 0x586: 0x4194, 0x587: 0x41b2, 0x588: 0x3cd4, 0x589: 0x3cdc, 0x58a: 0x40cc, 0x58b: 0x40ea, + 0x58c: 0x40d6, 0x58d: 0x40f4, 0x58e: 0x40e0, 0x58f: 0x40fe, 0x590: 0x3de4, 0x591: 0x3dec, + 0x592: 0x41bc, 0x593: 0x41da, 0x594: 0x41c6, 0x595: 0x41e4, 0x596: 0x41d0, 0x597: 0x41ee, + 0x598: 0x3d04, 0x599: 0x3d0c, 0x59a: 0x4108, 0x59b: 0x4126, 0x59c: 0x4112, 0x59d: 0x4130, + 0x59e: 0x411c, 0x59f: 0x413a, 0x5a0: 0x3ebc, 0x5a1: 0x3ec4, 0x5a2: 0x41f8, 0x5a3: 0x4216, + 0x5a4: 0x4202, 0x5a5: 0x4220, 0x5a6: 0x420c, 0x5a7: 0x422a, 0x5a8: 0x3d7c, 0x5a9: 0x3d84, + 0x5aa: 0x4144, 0x5ab: 0x4162, 0x5ac: 0x414e, 0x5ad: 0x416c, 0x5ae: 0x4158, 0x5af: 0x4176, + 0x5b0: 0x3681, 0x5b1: 0x367b, 0x5b2: 0x3d8c, 0x5b3: 0x3687, 0x5b4: 0x3d94, + 0x5b6: 0x48a2, 0x5b7: 0x3dac, 0x5b8: 0x35f1, 0x5b9: 0x35eb, 0x5ba: 0x35df, 0x5bb: 0x4380, + 0x5bc: 0x35f7, 0x5bd: 0x8100, 0x5be: 0x01d3, 0x5bf: 0xa100, + // Block 0x17, offset 0x5c0 + 0x5c0: 0x8100, 0x5c1: 0x35a3, 0x5c2: 0x3dd4, 0x5c3: 0x3699, 0x5c4: 0x3ddc, + 0x5c6: 0x48cc, 0x5c7: 0x3df4, 0x5c8: 0x35fd, 0x5c9: 0x4386, 0x5ca: 0x3609, 0x5cb: 0x438c, + 0x5cc: 0x3615, 0x5cd: 0x3b8b, 0x5ce: 0x3b92, 0x5cf: 0x3b99, 0x5d0: 0x36b1, 0x5d1: 0x36ab, + 0x5d2: 0x3dfc, 0x5d3: 0x4576, 0x5d6: 0x36b7, 0x5d7: 0x3e0c, + 0x5d8: 0x362d, 0x5d9: 0x3627, 0x5da: 0x361b, 0x5db: 0x4392, 0x5dd: 0x3ba0, + 0x5de: 0x3ba7, 0x5df: 0x3bae, 0x5e0: 0x36e7, 0x5e1: 0x36e1, 0x5e2: 0x3e64, 0x5e3: 0x457e, + 0x5e4: 0x36c9, 0x5e5: 0x36cf, 0x5e6: 0x36ed, 0x5e7: 0x3e74, 0x5e8: 0x365d, 0x5e9: 0x3657, + 0x5ea: 0x364b, 0x5eb: 0x439e, 0x5ec: 0x3645, 0x5ed: 0x3597, 0x5ee: 0x437a, 0x5ef: 0x0081, + 0x5f2: 0x3eac, 0x5f3: 0x36f3, 0x5f4: 0x3eb4, + 0x5f6: 0x491a, 0x5f7: 0x3ecc, 0x5f8: 0x3639, 0x5f9: 0x4398, 0x5fa: 0x3669, 0x5fb: 0x43aa, + 0x5fc: 0x3675, 0x5fd: 0x4252, 0x5fe: 0xa100, + // Block 0x18, offset 0x600 + 0x601: 0x3c02, 0x603: 0xa000, 0x604: 0x3c09, 0x605: 0xa000, + 0x607: 0x3c10, 0x608: 0xa000, 0x609: 0x3c17, + 0x60d: 0xa000, + 0x620: 0x2f61, 0x621: 0xa000, 0x622: 0x3c25, + 0x624: 0xa000, 0x625: 0xa000, + 0x62d: 0x3c1e, 0x62e: 0x2f5c, 0x62f: 0x2f66, + 0x630: 0x3c2c, 0x631: 0x3c33, 0x632: 0xa000, 0x633: 0xa000, 0x634: 0x3c3a, 0x635: 0x3c41, + 0x636: 0xa000, 0x637: 0xa000, 0x638: 0x3c48, 0x639: 0x3c4f, 0x63a: 0xa000, 0x63b: 0xa000, + 0x63c: 0xa000, 0x63d: 0xa000, + // Block 0x19, offset 0x640 + 0x640: 0x3c56, 0x641: 0x3c5d, 0x642: 0xa000, 0x643: 0xa000, 0x644: 0x3c72, 0x645: 0x3c79, + 0x646: 0xa000, 0x647: 0xa000, 0x648: 0x3c80, 0x649: 0x3c87, + 0x651: 0xa000, + 0x652: 0xa000, + 0x662: 0xa000, + 0x668: 0xa000, 0x669: 0xa000, + 0x66b: 0xa000, 0x66c: 0x3c9c, 0x66d: 0x3ca3, 0x66e: 0x3caa, 0x66f: 0x3cb1, + 0x672: 0xa000, 0x673: 0xa000, 0x674: 0xa000, 0x675: 0xa000, + // Block 0x1a, offset 0x680 + 0x686: 0xa000, 0x68b: 0xa000, + 0x68c: 0x3f04, 0x68d: 0xa000, 0x68e: 0x3f0c, 0x68f: 0xa000, 0x690: 0x3f14, 0x691: 0xa000, + 0x692: 0x3f1c, 0x693: 0xa000, 0x694: 0x3f24, 0x695: 0xa000, 0x696: 0x3f2c, 0x697: 0xa000, + 0x698: 0x3f34, 0x699: 0xa000, 0x69a: 0x3f3c, 0x69b: 0xa000, 0x69c: 0x3f44, 0x69d: 0xa000, + 0x69e: 0x3f4c, 0x69f: 0xa000, 0x6a0: 0x3f54, 0x6a1: 0xa000, 0x6a2: 0x3f5c, + 0x6a4: 0xa000, 0x6a5: 0x3f64, 0x6a6: 0xa000, 0x6a7: 0x3f6c, 0x6a8: 0xa000, 0x6a9: 0x3f74, + 0x6af: 0xa000, + 0x6b0: 0x3f7c, 0x6b1: 0x3f84, 0x6b2: 0xa000, 0x6b3: 0x3f8c, 0x6b4: 0x3f94, 0x6b5: 0xa000, + 0x6b6: 0x3f9c, 0x6b7: 0x3fa4, 0x6b8: 0xa000, 0x6b9: 0x3fac, 0x6ba: 0x3fb4, 0x6bb: 0xa000, + 0x6bc: 0x3fbc, 0x6bd: 0x3fc4, + // Block 0x1b, offset 0x6c0 + 0x6d4: 0x3efc, + 0x6d9: 0x9903, 0x6da: 0x9903, 0x6db: 0x8100, 0x6dc: 0x8100, 0x6dd: 0xa000, + 0x6de: 0x3fcc, + 0x6e6: 0xa000, + 0x6eb: 0xa000, 0x6ec: 0x3fdc, 0x6ed: 0xa000, 0x6ee: 0x3fe4, 0x6ef: 0xa000, + 0x6f0: 0x3fec, 0x6f1: 0xa000, 0x6f2: 0x3ff4, 0x6f3: 0xa000, 0x6f4: 0x3ffc, 0x6f5: 0xa000, + 0x6f6: 0x4004, 0x6f7: 0xa000, 0x6f8: 0x400c, 0x6f9: 0xa000, 0x6fa: 0x4014, 0x6fb: 0xa000, + 0x6fc: 0x401c, 0x6fd: 0xa000, 0x6fe: 0x4024, 0x6ff: 0xa000, + // Block 0x1c, offset 0x700 + 0x700: 0x402c, 0x701: 0xa000, 0x702: 0x4034, 0x704: 0xa000, 0x705: 0x403c, + 0x706: 0xa000, 0x707: 0x4044, 0x708: 0xa000, 0x709: 0x404c, + 0x70f: 0xa000, 0x710: 0x4054, 0x711: 0x405c, + 0x712: 0xa000, 0x713: 0x4064, 0x714: 0x406c, 0x715: 0xa000, 0x716: 0x4074, 0x717: 0x407c, + 0x718: 0xa000, 0x719: 0x4084, 0x71a: 0x408c, 0x71b: 0xa000, 0x71c: 0x4094, 0x71d: 0x409c, + 0x72f: 0xa000, + 0x730: 0xa000, 0x731: 0xa000, 0x732: 0xa000, 0x734: 0x3fd4, + 0x737: 0x40a4, 0x738: 0x40ac, 0x739: 0x40b4, 0x73a: 0x40bc, + 0x73d: 0xa000, 0x73e: 0x40c4, + // Block 0x1d, offset 0x740 + 0x740: 0x1377, 0x741: 0x0cfb, 0x742: 0x13d3, 0x743: 0x139f, 0x744: 0x0e57, 0x745: 0x06eb, + 0x746: 0x08df, 0x747: 0x1627, 0x748: 0x1627, 0x749: 0x0a0b, 0x74a: 0x145b, 0x74b: 0x0943, + 0x74c: 0x0a07, 0x74d: 0x0bef, 0x74e: 0x0fcf, 0x74f: 0x115f, 0x750: 0x1297, 0x751: 0x12d3, + 0x752: 0x1307, 0x753: 0x141b, 0x754: 0x0d73, 0x755: 0x0dff, 0x756: 0x0eab, 0x757: 0x0f43, + 0x758: 0x125f, 0x759: 0x1443, 0x75a: 0x156f, 0x75b: 0x070f, 0x75c: 0x08b3, 0x75d: 0x0d87, + 0x75e: 0x0ecf, 0x75f: 0x1293, 0x760: 0x15bf, 0x761: 0x0ab3, 0x762: 0x0e77, 0x763: 0x1283, + 0x764: 0x1317, 0x765: 0x0c23, 0x766: 0x11bb, 0x767: 0x12df, 0x768: 0x0b1f, 0x769: 0x0d0f, + 0x76a: 0x0e17, 0x76b: 0x0f1b, 0x76c: 0x1427, 0x76d: 0x074f, 0x76e: 0x07e7, 0x76f: 0x0853, + 0x770: 0x0c8b, 0x771: 0x0d7f, 0x772: 0x0ecb, 0x773: 0x0fef, 0x774: 0x1177, 0x775: 0x128b, + 0x776: 0x12a3, 0x777: 0x13c7, 0x778: 0x14eb, 0x779: 0x159f, 0x77a: 0x15bb, 0x77b: 0x102b, + 0x77c: 0x106b, 0x77d: 0x1123, 0x77e: 0x1243, 0x77f: 0x1477, + // Block 0x1e, offset 0x780 + 0x780: 0x15c7, 0x781: 0x134b, 0x782: 0x09c7, 0x783: 0x0b3b, 0x784: 0x10db, 0x785: 0x119b, + 0x786: 0x0eff, 0x787: 0x1033, 0x788: 0x1397, 0x789: 0x14e3, 0x78a: 0x09c3, 0x78b: 0x0a8f, + 0x78c: 0x0d77, 0x78d: 0x0e2b, 0x78e: 0x0e5f, 0x78f: 0x1113, 0x790: 0x113b, 0x791: 0x14a3, + 0x792: 0x084f, 0x793: 0x11a7, 0x794: 0x07f3, 0x795: 0x07ef, 0x796: 0x1097, 0x797: 0x1127, + 0x798: 0x125b, 0x799: 0x14ab, 0x79a: 0x1367, 0x79b: 0x0c27, 0x79c: 0x0d73, 0x79d: 0x1357, + 0x79e: 0x06f7, 0x79f: 0x0a63, 0x7a0: 0x0b93, 0x7a1: 0x0f2f, 0x7a2: 0x0faf, 0x7a3: 0x0873, + 0x7a4: 0x103b, 0x7a5: 0x075f, 0x7a6: 0x0b77, 0x7a7: 0x06d7, 0x7a8: 0x0deb, 0x7a9: 0x0ca3, + 0x7aa: 0x110f, 0x7ab: 0x08c7, 0x7ac: 0x09b3, 0x7ad: 0x0ffb, 0x7ae: 0x1263, 0x7af: 0x133b, + 0x7b0: 0x0db7, 0x7b1: 0x13f7, 0x7b2: 0x0de3, 0x7b3: 0x0c37, 0x7b4: 0x121b, 0x7b5: 0x0c57, + 0x7b6: 0x0fab, 0x7b7: 0x072b, 0x7b8: 0x07a7, 0x7b9: 0x07eb, 0x7ba: 0x0d53, 0x7bb: 0x10fb, + 0x7bc: 0x11f3, 0x7bd: 0x1347, 0x7be: 0x1457, 0x7bf: 0x085b, + // Block 0x1f, offset 0x7c0 + 0x7c0: 0x090f, 0x7c1: 0x0a17, 0x7c2: 0x0b2f, 0x7c3: 0x0cbf, 0x7c4: 0x0e7b, 0x7c5: 0x103f, + 0x7c6: 0x1493, 0x7c7: 0x1577, 0x7c8: 0x15cb, 0x7c9: 0x15e3, 0x7ca: 0x0837, 0x7cb: 0x0cf3, + 0x7cc: 0x0da3, 0x7cd: 0x13eb, 0x7ce: 0x0afb, 0x7cf: 0x0bd7, 0x7d0: 0x0bf3, 0x7d1: 0x0c83, + 0x7d2: 0x0e6b, 0x7d3: 0x0eb7, 0x7d4: 0x0f67, 0x7d5: 0x108b, 0x7d6: 0x112f, 0x7d7: 0x1193, + 0x7d8: 0x13db, 0x7d9: 0x126b, 0x7da: 0x1403, 0x7db: 0x147b, 0x7dc: 0x080f, 0x7dd: 0x083b, + 0x7de: 0x0923, 0x7df: 0x0ea7, 0x7e0: 0x12f3, 0x7e1: 0x133b, 0x7e2: 0x0b1b, 0x7e3: 0x0b8b, + 0x7e4: 0x0c4f, 0x7e5: 0x0daf, 0x7e6: 0x10d7, 0x7e7: 0x0f23, 0x7e8: 0x073b, 0x7e9: 0x097f, + 0x7ea: 0x0a63, 0x7eb: 0x0ac7, 0x7ec: 0x0b97, 0x7ed: 0x0f3f, 0x7ee: 0x0f5b, 0x7ef: 0x116b, + 0x7f0: 0x118b, 0x7f1: 0x145f, 0x7f2: 0x14df, 0x7f3: 0x14ef, 0x7f4: 0x152b, 0x7f5: 0x0753, + 0x7f6: 0x107f, 0x7f7: 0x144b, 0x7f8: 0x14c7, 0x7f9: 0x0baf, 0x7fa: 0x0717, 0x7fb: 0x0777, + 0x7fc: 0x0a67, 0x7fd: 0x0a87, 0x7fe: 0x0caf, 0x7ff: 0x0d73, + // Block 0x20, offset 0x800 + 0x800: 0x0ec3, 0x801: 0x0fcb, 0x802: 0x1277, 0x803: 0x1417, 0x804: 0x161f, 0x805: 0x0ce3, + 0x806: 0x149f, 0x807: 0x0833, 0x808: 0x0d2f, 0x809: 0x0d3b, 0x80a: 0x0e0f, 0x80b: 0x0e47, + 0x80c: 0x0f4b, 0x80d: 0x0fa7, 0x80e: 0x1027, 0x80f: 0x110b, 0x810: 0x1537, 0x811: 0x07af, + 0x812: 0x0c03, 0x813: 0x14af, 0x814: 0x0767, 0x815: 0x0aab, 0x816: 0x0e2f, 0x817: 0x13df, + 0x818: 0x0b67, 0x819: 0x0bb7, 0x81a: 0x0d43, 0x81b: 0x0f2f, 0x81c: 0x14b7, 0x81d: 0x0817, + 0x81e: 0x08ff, 0x81f: 0x0a97, 0x820: 0x0cd3, 0x821: 0x0d1f, 0x822: 0x0d5f, 0x823: 0x0df3, + 0x824: 0x0f47, 0x825: 0x0fbb, 0x826: 0x1157, 0x827: 0x12f7, 0x828: 0x1303, 0x829: 0x1453, + 0x82a: 0x14d3, 0x82b: 0x0883, 0x82c: 0x0e4b, 0x82d: 0x0903, 0x82e: 0x0ec7, 0x82f: 0x0f6b, + 0x830: 0x1287, 0x831: 0x14bb, 0x832: 0x15a7, 0x833: 0x15cf, 0x834: 0x0d37, 0x835: 0x0e27, + 0x836: 0x11c3, 0x837: 0x10b7, 0x838: 0x10c3, 0x839: 0x10e7, 0x83a: 0x0f17, 0x83b: 0x0e9f, + 0x83c: 0x1363, 0x83d: 0x0733, 0x83e: 0x122b, 0x83f: 0x081b, + // Block 0x21, offset 0x840 + 0x840: 0x080b, 0x841: 0x0b0b, 0x842: 0x0c2b, 0x843: 0x10f3, 0x844: 0x0a53, 0x845: 0x0e03, + 0x846: 0x0cef, 0x847: 0x13e7, 0x848: 0x12e7, 0x849: 0x14a7, 0x84a: 0x1323, 0x84b: 0x0b27, + 0x84c: 0x0787, 0x84d: 0x095b, 0x850: 0x09af, + 0x852: 0x0cdf, 0x855: 0x07f7, 0x856: 0x0f1f, 0x857: 0x0fe3, + 0x858: 0x1047, 0x859: 0x1063, 0x85a: 0x1067, 0x85b: 0x107b, 0x85c: 0x14f7, 0x85d: 0x10eb, + 0x85e: 0x116f, 0x860: 0x128f, 0x862: 0x1353, + 0x865: 0x1407, 0x866: 0x1433, + 0x86a: 0x154b, 0x86b: 0x154f, 0x86c: 0x1553, 0x86d: 0x15b7, 0x86e: 0x142b, 0x86f: 0x14c3, + 0x870: 0x0757, 0x871: 0x077b, 0x872: 0x078f, 0x873: 0x084b, 0x874: 0x0857, 0x875: 0x0897, + 0x876: 0x094b, 0x877: 0x0967, 0x878: 0x096f, 0x879: 0x09ab, 0x87a: 0x09b7, 0x87b: 0x0a93, + 0x87c: 0x0a9b, 0x87d: 0x0ba3, 0x87e: 0x0bcb, 0x87f: 0x0bd3, + // Block 0x22, offset 0x880 + 0x880: 0x0beb, 0x881: 0x0c97, 0x882: 0x0cc7, 0x883: 0x0ce7, 0x884: 0x0d57, 0x885: 0x0e1b, + 0x886: 0x0e37, 0x887: 0x0e67, 0x888: 0x0ebb, 0x889: 0x0edb, 0x88a: 0x0f4f, 0x88b: 0x102f, + 0x88c: 0x104b, 0x88d: 0x1053, 0x88e: 0x104f, 0x88f: 0x1057, 0x890: 0x105b, 0x891: 0x105f, + 0x892: 0x1073, 0x893: 0x1077, 0x894: 0x109b, 0x895: 0x10af, 0x896: 0x10cb, 0x897: 0x112f, + 0x898: 0x1137, 0x899: 0x113f, 0x89a: 0x1153, 0x89b: 0x117b, 0x89c: 0x11cb, 0x89d: 0x11ff, + 0x89e: 0x11ff, 0x89f: 0x1267, 0x8a0: 0x130f, 0x8a1: 0x1327, 0x8a2: 0x135b, 0x8a3: 0x135f, + 0x8a4: 0x13a3, 0x8a5: 0x13a7, 0x8a6: 0x13ff, 0x8a7: 0x1407, 0x8a8: 0x14d7, 0x8a9: 0x151b, + 0x8aa: 0x1533, 0x8ab: 0x0b9b, 0x8ac: 0x171a, 0x8ad: 0x11e3, + 0x8b0: 0x06df, 0x8b1: 0x07e3, 0x8b2: 0x07a3, 0x8b3: 0x074b, 0x8b4: 0x078b, 0x8b5: 0x07b7, + 0x8b6: 0x0847, 0x8b7: 0x0863, 0x8b8: 0x094b, 0x8b9: 0x0937, 0x8ba: 0x0947, 0x8bb: 0x0963, + 0x8bc: 0x09af, 0x8bd: 0x09bf, 0x8be: 0x0a03, 0x8bf: 0x0a0f, + // Block 0x23, offset 0x8c0 + 0x8c0: 0x0a2b, 0x8c1: 0x0a3b, 0x8c2: 0x0b23, 0x8c3: 0x0b2b, 0x8c4: 0x0b5b, 0x8c5: 0x0b7b, + 0x8c6: 0x0bab, 0x8c7: 0x0bc3, 0x8c8: 0x0bb3, 0x8c9: 0x0bd3, 0x8ca: 0x0bc7, 0x8cb: 0x0beb, + 0x8cc: 0x0c07, 0x8cd: 0x0c5f, 0x8ce: 0x0c6b, 0x8cf: 0x0c73, 0x8d0: 0x0c9b, 0x8d1: 0x0cdf, + 0x8d2: 0x0d0f, 0x8d3: 0x0d13, 0x8d4: 0x0d27, 0x8d5: 0x0da7, 0x8d6: 0x0db7, 0x8d7: 0x0e0f, + 0x8d8: 0x0e5b, 0x8d9: 0x0e53, 0x8da: 0x0e67, 0x8db: 0x0e83, 0x8dc: 0x0ebb, 0x8dd: 0x1013, + 0x8de: 0x0edf, 0x8df: 0x0f13, 0x8e0: 0x0f1f, 0x8e1: 0x0f5f, 0x8e2: 0x0f7b, 0x8e3: 0x0f9f, + 0x8e4: 0x0fc3, 0x8e5: 0x0fc7, 0x8e6: 0x0fe3, 0x8e7: 0x0fe7, 0x8e8: 0x0ff7, 0x8e9: 0x100b, + 0x8ea: 0x1007, 0x8eb: 0x1037, 0x8ec: 0x10b3, 0x8ed: 0x10cb, 0x8ee: 0x10e3, 0x8ef: 0x111b, + 0x8f0: 0x112f, 0x8f1: 0x114b, 0x8f2: 0x117b, 0x8f3: 0x122f, 0x8f4: 0x1257, 0x8f5: 0x12cb, + 0x8f6: 0x1313, 0x8f7: 0x131f, 0x8f8: 0x1327, 0x8f9: 0x133f, 0x8fa: 0x1353, 0x8fb: 0x1343, + 0x8fc: 0x135b, 0x8fd: 0x1357, 0x8fe: 0x134f, 0x8ff: 0x135f, + // Block 0x24, offset 0x900 + 0x900: 0x136b, 0x901: 0x13a7, 0x902: 0x13e3, 0x903: 0x1413, 0x904: 0x1447, 0x905: 0x1467, + 0x906: 0x14b3, 0x907: 0x14d7, 0x908: 0x14f7, 0x909: 0x150b, 0x90a: 0x151b, 0x90b: 0x1527, + 0x90c: 0x1533, 0x90d: 0x1587, 0x90e: 0x1627, 0x90f: 0x16b1, 0x910: 0x16ac, 0x911: 0x16de, + 0x912: 0x0607, 0x913: 0x062f, 0x914: 0x0633, 0x915: 0x1760, 0x916: 0x178d, 0x917: 0x1805, + 0x918: 0x1613, 0x919: 0x1623, + // Block 0x25, offset 0x940 + 0x940: 0x06fb, 0x941: 0x06f3, 0x942: 0x0703, 0x943: 0x1643, 0x944: 0x0747, 0x945: 0x0757, + 0x946: 0x075b, 0x947: 0x0763, 0x948: 0x076b, 0x949: 0x076f, 0x94a: 0x077b, 0x94b: 0x0773, + 0x94c: 0x05b3, 0x94d: 0x1657, 0x94e: 0x078f, 0x94f: 0x0793, 0x950: 0x0797, 0x951: 0x07b3, + 0x952: 0x1648, 0x953: 0x05b7, 0x954: 0x079f, 0x955: 0x07bf, 0x956: 0x1652, 0x957: 0x07cf, + 0x958: 0x07d7, 0x959: 0x0737, 0x95a: 0x07df, 0x95b: 0x07e3, 0x95c: 0x182d, 0x95d: 0x07ff, + 0x95e: 0x0807, 0x95f: 0x05bf, 0x960: 0x081f, 0x961: 0x0823, 0x962: 0x082b, 0x963: 0x082f, + 0x964: 0x05c3, 0x965: 0x0847, 0x966: 0x084b, 0x967: 0x0857, 0x968: 0x0863, 0x969: 0x0867, + 0x96a: 0x086b, 0x96b: 0x0873, 0x96c: 0x0893, 0x96d: 0x0897, 0x96e: 0x089f, 0x96f: 0x08af, + 0x970: 0x08b7, 0x971: 0x08bb, 0x972: 0x08bb, 0x973: 0x08bb, 0x974: 0x1666, 0x975: 0x0e93, + 0x976: 0x08cf, 0x977: 0x08d7, 0x978: 0x166b, 0x979: 0x08e3, 0x97a: 0x08eb, 0x97b: 0x08f3, + 0x97c: 0x091b, 0x97d: 0x0907, 0x97e: 0x0913, 0x97f: 0x0917, + // Block 0x26, offset 0x980 + 0x980: 0x091f, 0x981: 0x0927, 0x982: 0x092b, 0x983: 0x0933, 0x984: 0x093b, 0x985: 0x093f, + 0x986: 0x093f, 0x987: 0x0947, 0x988: 0x094f, 0x989: 0x0953, 0x98a: 0x095f, 0x98b: 0x0983, + 0x98c: 0x0967, 0x98d: 0x0987, 0x98e: 0x096b, 0x98f: 0x0973, 0x990: 0x080b, 0x991: 0x09cf, + 0x992: 0x0997, 0x993: 0x099b, 0x994: 0x099f, 0x995: 0x0993, 0x996: 0x09a7, 0x997: 0x09a3, + 0x998: 0x09bb, 0x999: 0x1670, 0x99a: 0x09d7, 0x99b: 0x09db, 0x99c: 0x09e3, 0x99d: 0x09ef, + 0x99e: 0x09f7, 0x99f: 0x0a13, 0x9a0: 0x1675, 0x9a1: 0x167a, 0x9a2: 0x0a1f, 0x9a3: 0x0a23, + 0x9a4: 0x0a27, 0x9a5: 0x0a1b, 0x9a6: 0x0a2f, 0x9a7: 0x05c7, 0x9a8: 0x05cb, 0x9a9: 0x0a37, + 0x9aa: 0x0a3f, 0x9ab: 0x0a3f, 0x9ac: 0x167f, 0x9ad: 0x0a5b, 0x9ae: 0x0a5f, 0x9af: 0x0a63, + 0x9b0: 0x0a6b, 0x9b1: 0x1684, 0x9b2: 0x0a73, 0x9b3: 0x0a77, 0x9b4: 0x0b4f, 0x9b5: 0x0a7f, + 0x9b6: 0x05cf, 0x9b7: 0x0a8b, 0x9b8: 0x0a9b, 0x9b9: 0x0aa7, 0x9ba: 0x0aa3, 0x9bb: 0x168e, + 0x9bc: 0x0aaf, 0x9bd: 0x1693, 0x9be: 0x0abb, 0x9bf: 0x0ab7, + // Block 0x27, offset 0x9c0 + 0x9c0: 0x0abf, 0x9c1: 0x0acf, 0x9c2: 0x0ad3, 0x9c3: 0x05d3, 0x9c4: 0x0ae3, 0x9c5: 0x0aeb, + 0x9c6: 0x0aef, 0x9c7: 0x0af3, 0x9c8: 0x05d7, 0x9c9: 0x1698, 0x9ca: 0x05db, 0x9cb: 0x0b0f, + 0x9cc: 0x0b13, 0x9cd: 0x0b17, 0x9ce: 0x0b1f, 0x9cf: 0x185f, 0x9d0: 0x0b37, 0x9d1: 0x16a2, + 0x9d2: 0x16a2, 0x9d3: 0x11d7, 0x9d4: 0x0b47, 0x9d5: 0x0b47, 0x9d6: 0x05df, 0x9d7: 0x16c5, + 0x9d8: 0x1797, 0x9d9: 0x0b57, 0x9da: 0x0b5f, 0x9db: 0x05e3, 0x9dc: 0x0b73, 0x9dd: 0x0b83, + 0x9de: 0x0b87, 0x9df: 0x0b8f, 0x9e0: 0x0b9f, 0x9e1: 0x05eb, 0x9e2: 0x05e7, 0x9e3: 0x0ba3, + 0x9e4: 0x16a7, 0x9e5: 0x0ba7, 0x9e6: 0x0bbb, 0x9e7: 0x0bbf, 0x9e8: 0x0bc3, 0x9e9: 0x0bbf, + 0x9ea: 0x0bcf, 0x9eb: 0x0bd3, 0x9ec: 0x0be3, 0x9ed: 0x0bdb, 0x9ee: 0x0bdf, 0x9ef: 0x0be7, + 0x9f0: 0x0beb, 0x9f1: 0x0bef, 0x9f2: 0x0bfb, 0x9f3: 0x0bff, 0x9f4: 0x0c17, 0x9f5: 0x0c1f, + 0x9f6: 0x0c2f, 0x9f7: 0x0c43, 0x9f8: 0x16b6, 0x9f9: 0x0c3f, 0x9fa: 0x0c33, 0x9fb: 0x0c4b, + 0x9fc: 0x0c53, 0x9fd: 0x0c67, 0x9fe: 0x16bb, 0x9ff: 0x0c6f, + // Block 0x28, offset 0xa00 + 0xa00: 0x0c63, 0xa01: 0x0c5b, 0xa02: 0x05ef, 0xa03: 0x0c77, 0xa04: 0x0c7f, 0xa05: 0x0c87, + 0xa06: 0x0c7b, 0xa07: 0x05f3, 0xa08: 0x0c97, 0xa09: 0x0c9f, 0xa0a: 0x16c0, 0xa0b: 0x0ccb, + 0xa0c: 0x0cff, 0xa0d: 0x0cdb, 0xa0e: 0x05ff, 0xa0f: 0x0ce7, 0xa10: 0x05fb, 0xa11: 0x05f7, + 0xa12: 0x07c3, 0xa13: 0x07c7, 0xa14: 0x0d03, 0xa15: 0x0ceb, 0xa16: 0x11ab, 0xa17: 0x0663, + 0xa18: 0x0d0f, 0xa19: 0x0d13, 0xa1a: 0x0d17, 0xa1b: 0x0d2b, 0xa1c: 0x0d23, 0xa1d: 0x16d9, + 0xa1e: 0x0603, 0xa1f: 0x0d3f, 0xa20: 0x0d33, 0xa21: 0x0d4f, 0xa22: 0x0d57, 0xa23: 0x16e3, + 0xa24: 0x0d5b, 0xa25: 0x0d47, 0xa26: 0x0d63, 0xa27: 0x0607, 0xa28: 0x0d67, 0xa29: 0x0d6b, + 0xa2a: 0x0d6f, 0xa2b: 0x0d7b, 0xa2c: 0x16e8, 0xa2d: 0x0d83, 0xa2e: 0x060b, 0xa2f: 0x0d8f, + 0xa30: 0x16ed, 0xa31: 0x0d93, 0xa32: 0x060f, 0xa33: 0x0d9f, 0xa34: 0x0dab, 0xa35: 0x0db7, + 0xa36: 0x0dbb, 0xa37: 0x16f2, 0xa38: 0x1689, 0xa39: 0x16f7, 0xa3a: 0x0ddb, 0xa3b: 0x16fc, + 0xa3c: 0x0de7, 0xa3d: 0x0def, 0xa3e: 0x0ddf, 0xa3f: 0x0dfb, + // Block 0x29, offset 0xa40 + 0xa40: 0x0e0b, 0xa41: 0x0e1b, 0xa42: 0x0e0f, 0xa43: 0x0e13, 0xa44: 0x0e1f, 0xa45: 0x0e23, + 0xa46: 0x1701, 0xa47: 0x0e07, 0xa48: 0x0e3b, 0xa49: 0x0e3f, 0xa4a: 0x0613, 0xa4b: 0x0e53, + 0xa4c: 0x0e4f, 0xa4d: 0x1706, 0xa4e: 0x0e33, 0xa4f: 0x0e6f, 0xa50: 0x170b, 0xa51: 0x1710, + 0xa52: 0x0e73, 0xa53: 0x0e87, 0xa54: 0x0e83, 0xa55: 0x0e7f, 0xa56: 0x0617, 0xa57: 0x0e8b, + 0xa58: 0x0e9b, 0xa59: 0x0e97, 0xa5a: 0x0ea3, 0xa5b: 0x164d, 0xa5c: 0x0eb3, 0xa5d: 0x1715, + 0xa5e: 0x0ebf, 0xa5f: 0x171f, 0xa60: 0x0ed3, 0xa61: 0x0edf, 0xa62: 0x0ef3, 0xa63: 0x1724, + 0xa64: 0x0f07, 0xa65: 0x0f0b, 0xa66: 0x1729, 0xa67: 0x172e, 0xa68: 0x0f27, 0xa69: 0x0f37, + 0xa6a: 0x061b, 0xa6b: 0x0f3b, 0xa6c: 0x061f, 0xa6d: 0x061f, 0xa6e: 0x0f53, 0xa6f: 0x0f57, + 0xa70: 0x0f5f, 0xa71: 0x0f63, 0xa72: 0x0f6f, 0xa73: 0x0623, 0xa74: 0x0f87, 0xa75: 0x1733, + 0xa76: 0x0fa3, 0xa77: 0x1738, 0xa78: 0x0faf, 0xa79: 0x169d, 0xa7a: 0x0fbf, 0xa7b: 0x173d, + 0xa7c: 0x1742, 0xa7d: 0x1747, 0xa7e: 0x0627, 0xa7f: 0x062b, + // Block 0x2a, offset 0xa80 + 0xa80: 0x0ff7, 0xa81: 0x1751, 0xa82: 0x174c, 0xa83: 0x1756, 0xa84: 0x175b, 0xa85: 0x0fff, + 0xa86: 0x1003, 0xa87: 0x1003, 0xa88: 0x100b, 0xa89: 0x0633, 0xa8a: 0x100f, 0xa8b: 0x0637, + 0xa8c: 0x063b, 0xa8d: 0x1765, 0xa8e: 0x1023, 0xa8f: 0x102b, 0xa90: 0x1037, 0xa91: 0x063f, + 0xa92: 0x176a, 0xa93: 0x105b, 0xa94: 0x176f, 0xa95: 0x1774, 0xa96: 0x107b, 0xa97: 0x1093, + 0xa98: 0x0643, 0xa99: 0x109b, 0xa9a: 0x109f, 0xa9b: 0x10a3, 0xa9c: 0x1779, 0xa9d: 0x177e, + 0xa9e: 0x177e, 0xa9f: 0x10bb, 0xaa0: 0x0647, 0xaa1: 0x1783, 0xaa2: 0x10cf, 0xaa3: 0x10d3, + 0xaa4: 0x064b, 0xaa5: 0x1788, 0xaa6: 0x10ef, 0xaa7: 0x064f, 0xaa8: 0x10ff, 0xaa9: 0x10f7, + 0xaaa: 0x1107, 0xaab: 0x1792, 0xaac: 0x111f, 0xaad: 0x0653, 0xaae: 0x112b, 0xaaf: 0x1133, + 0xab0: 0x1143, 0xab1: 0x0657, 0xab2: 0x179c, 0xab3: 0x17a1, 0xab4: 0x065b, 0xab5: 0x17a6, + 0xab6: 0x115b, 0xab7: 0x17ab, 0xab8: 0x1167, 0xab9: 0x1173, 0xaba: 0x117b, 0xabb: 0x17b0, + 0xabc: 0x17b5, 0xabd: 0x118f, 0xabe: 0x17ba, 0xabf: 0x1197, + // Block 0x2b, offset 0xac0 + 0xac0: 0x16ca, 0xac1: 0x065f, 0xac2: 0x11af, 0xac3: 0x11b3, 0xac4: 0x0667, 0xac5: 0x11b7, + 0xac6: 0x0a33, 0xac7: 0x17bf, 0xac8: 0x17c4, 0xac9: 0x16cf, 0xaca: 0x16d4, 0xacb: 0x11d7, + 0xacc: 0x11db, 0xacd: 0x13f3, 0xace: 0x066b, 0xacf: 0x1207, 0xad0: 0x1203, 0xad1: 0x120b, + 0xad2: 0x083f, 0xad3: 0x120f, 0xad4: 0x1213, 0xad5: 0x1217, 0xad6: 0x121f, 0xad7: 0x17c9, + 0xad8: 0x121b, 0xad9: 0x1223, 0xada: 0x1237, 0xadb: 0x123b, 0xadc: 0x1227, 0xadd: 0x123f, + 0xade: 0x1253, 0xadf: 0x1267, 0xae0: 0x1233, 0xae1: 0x1247, 0xae2: 0x124b, 0xae3: 0x124f, + 0xae4: 0x17ce, 0xae5: 0x17d8, 0xae6: 0x17d3, 0xae7: 0x066f, 0xae8: 0x126f, 0xae9: 0x1273, + 0xaea: 0x127b, 0xaeb: 0x17ec, 0xaec: 0x127f, 0xaed: 0x17dd, 0xaee: 0x0673, 0xaef: 0x0677, + 0xaf0: 0x17e2, 0xaf1: 0x17e7, 0xaf2: 0x067b, 0xaf3: 0x129f, 0xaf4: 0x12a3, 0xaf5: 0x12a7, + 0xaf6: 0x12ab, 0xaf7: 0x12b7, 0xaf8: 0x12b3, 0xaf9: 0x12bf, 0xafa: 0x12bb, 0xafb: 0x12cb, + 0xafc: 0x12c3, 0xafd: 0x12c7, 0xafe: 0x12cf, 0xaff: 0x067f, + // Block 0x2c, offset 0xb00 + 0xb00: 0x12d7, 0xb01: 0x12db, 0xb02: 0x0683, 0xb03: 0x12eb, 0xb04: 0x12ef, 0xb05: 0x17f1, + 0xb06: 0x12fb, 0xb07: 0x12ff, 0xb08: 0x0687, 0xb09: 0x130b, 0xb0a: 0x05bb, 0xb0b: 0x17f6, + 0xb0c: 0x17fb, 0xb0d: 0x068b, 0xb0e: 0x068f, 0xb0f: 0x1337, 0xb10: 0x134f, 0xb11: 0x136b, + 0xb12: 0x137b, 0xb13: 0x1800, 0xb14: 0x138f, 0xb15: 0x1393, 0xb16: 0x13ab, 0xb17: 0x13b7, + 0xb18: 0x180a, 0xb19: 0x165c, 0xb1a: 0x13c3, 0xb1b: 0x13bf, 0xb1c: 0x13cb, 0xb1d: 0x1661, + 0xb1e: 0x13d7, 0xb1f: 0x13e3, 0xb20: 0x180f, 0xb21: 0x1814, 0xb22: 0x1423, 0xb23: 0x142f, + 0xb24: 0x1437, 0xb25: 0x1819, 0xb26: 0x143b, 0xb27: 0x1463, 0xb28: 0x146f, 0xb29: 0x1473, + 0xb2a: 0x146b, 0xb2b: 0x147f, 0xb2c: 0x1483, 0xb2d: 0x181e, 0xb2e: 0x148f, 0xb2f: 0x0693, + 0xb30: 0x1497, 0xb31: 0x1823, 0xb32: 0x0697, 0xb33: 0x14cf, 0xb34: 0x0ac3, 0xb35: 0x14e7, + 0xb36: 0x1828, 0xb37: 0x1832, 0xb38: 0x069b, 0xb39: 0x069f, 0xb3a: 0x150f, 0xb3b: 0x1837, + 0xb3c: 0x06a3, 0xb3d: 0x183c, 0xb3e: 0x1527, 0xb3f: 0x1527, + // Block 0x2d, offset 0xb40 + 0xb40: 0x152f, 0xb41: 0x1841, 0xb42: 0x1547, 0xb43: 0x06a7, 0xb44: 0x1557, 0xb45: 0x1563, + 0xb46: 0x156b, 0xb47: 0x1573, 0xb48: 0x06ab, 0xb49: 0x1846, 0xb4a: 0x1587, 0xb4b: 0x15a3, + 0xb4c: 0x15af, 0xb4d: 0x06af, 0xb4e: 0x06b3, 0xb4f: 0x15b3, 0xb50: 0x184b, 0xb51: 0x06b7, + 0xb52: 0x1850, 0xb53: 0x1855, 0xb54: 0x185a, 0xb55: 0x15d7, 0xb56: 0x06bb, 0xb57: 0x15eb, + 0xb58: 0x15f3, 0xb59: 0x15f7, 0xb5a: 0x15ff, 0xb5b: 0x1607, 0xb5c: 0x160f, 0xb5d: 0x1864, +} + +// nfcIndex: 22 blocks, 1408 entries, 1408 bytes +// Block 0 is the zero block. +var nfcIndex = [1408]uint8{ + // Block 0x0, offset 0x0 + // Block 0x1, offset 0x40 + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0xc2: 0x2c, 0xc3: 0x01, 0xc4: 0x02, 0xc5: 0x03, 0xc6: 0x2d, 0xc7: 0x04, + 0xc8: 0x05, 0xca: 0x2e, 0xcb: 0x2f, 0xcc: 0x06, 0xcd: 0x07, 0xce: 0x08, 0xcf: 0x30, + 0xd0: 0x09, 0xd1: 0x31, 0xd2: 0x32, 0xd3: 0x0a, 0xd6: 0x0b, 0xd7: 0x33, + 0xd8: 0x34, 0xd9: 0x0c, 0xdb: 0x35, 0xdc: 0x36, 0xdd: 0x37, 0xdf: 0x38, + 0xe0: 0x02, 0xe1: 0x03, 0xe2: 0x04, 0xe3: 0x05, + 0xea: 0x06, 0xeb: 0x07, 0xec: 0x08, 0xed: 0x09, 0xef: 0x0a, + 0xf0: 0x13, + // Block 0x4, offset 0x100 + 0x120: 0x39, 0x121: 0x3a, 0x123: 0x3b, 0x124: 0x3c, 0x125: 0x3d, 0x126: 0x3e, 0x127: 0x3f, + 0x128: 0x40, 0x129: 0x41, 0x12a: 0x42, 0x12b: 0x43, 0x12c: 0x3e, 0x12d: 0x44, 0x12e: 0x45, 0x12f: 0x46, + 0x131: 0x47, 0x132: 0x48, 0x133: 0x49, 0x134: 0x4a, 0x135: 0x4b, 0x137: 0x4c, + 0x138: 0x4d, 0x139: 0x4e, 0x13a: 0x4f, 0x13b: 0x50, 0x13c: 0x51, 0x13d: 0x52, 0x13e: 0x53, 0x13f: 0x54, + // Block 0x5, offset 0x140 + 0x140: 0x55, 0x142: 0x56, 0x144: 0x57, 0x145: 0x58, 0x146: 0x59, 0x147: 0x5a, + 0x14d: 0x5b, + 0x15c: 0x5c, 0x15f: 0x5d, + 0x162: 0x5e, 0x164: 0x5f, + 0x168: 0x60, 0x169: 0x61, 0x16a: 0x62, 0x16c: 0x0d, 0x16d: 0x63, 0x16e: 0x64, 0x16f: 0x65, + 0x170: 0x66, 0x173: 0x67, 0x177: 0x68, + 0x178: 0x0e, 0x179: 0x0f, 0x17a: 0x10, 0x17b: 0x11, 0x17c: 0x12, 0x17d: 0x13, 0x17e: 0x14, 0x17f: 0x15, + // Block 0x6, offset 0x180 + 0x180: 0x69, 0x183: 0x6a, 0x184: 0x6b, 0x186: 0x6c, 0x187: 0x6d, + 0x188: 0x6e, 0x189: 0x16, 0x18a: 0x17, 0x18b: 0x6f, 0x18c: 0x70, + 0x1ab: 0x71, + 0x1b3: 0x72, 0x1b5: 0x73, 0x1b7: 0x74, + // Block 0x7, offset 0x1c0 + 0x1c0: 0x75, 0x1c1: 0x18, 0x1c2: 0x19, 0x1c3: 0x1a, 0x1c4: 0x76, 0x1c5: 0x77, + 0x1c9: 0x78, 0x1cc: 0x79, 0x1cd: 0x7a, + // Block 0x8, offset 0x200 + 0x219: 0x7b, 0x21a: 0x7c, 0x21b: 0x7d, + 0x220: 0x7e, 0x223: 0x7f, 0x224: 0x80, 0x225: 0x81, 0x226: 0x82, 0x227: 0x83, + 0x22a: 0x84, 0x22b: 0x85, 0x22f: 0x86, + 0x230: 0x87, 0x231: 0x88, 0x232: 0x89, 0x233: 0x8a, 0x234: 0x8b, 0x235: 0x8c, 0x236: 0x8d, 0x237: 0x87, + 0x238: 0x88, 0x239: 0x89, 0x23a: 0x8a, 0x23b: 0x8b, 0x23c: 0x8c, 0x23d: 0x8d, 0x23e: 0x87, 0x23f: 0x88, + // Block 0x9, offset 0x240 + 0x240: 0x89, 0x241: 0x8a, 0x242: 0x8b, 0x243: 0x8c, 0x244: 0x8d, 0x245: 0x87, 0x246: 0x88, 0x247: 0x89, + 0x248: 0x8a, 0x249: 0x8b, 0x24a: 0x8c, 0x24b: 0x8d, 0x24c: 0x87, 0x24d: 0x88, 0x24e: 0x89, 0x24f: 0x8a, + 0x250: 0x8b, 0x251: 0x8c, 0x252: 0x8d, 0x253: 0x87, 0x254: 0x88, 0x255: 0x89, 0x256: 0x8a, 0x257: 0x8b, + 0x258: 0x8c, 0x259: 0x8d, 0x25a: 0x87, 0x25b: 0x88, 0x25c: 0x89, 0x25d: 0x8a, 0x25e: 0x8b, 0x25f: 0x8c, + 0x260: 0x8d, 0x261: 0x87, 0x262: 0x88, 0x263: 0x89, 0x264: 0x8a, 0x265: 0x8b, 0x266: 0x8c, 0x267: 0x8d, + 0x268: 0x87, 0x269: 0x88, 0x26a: 0x89, 0x26b: 0x8a, 0x26c: 0x8b, 0x26d: 0x8c, 0x26e: 0x8d, 0x26f: 0x87, + 0x270: 0x88, 0x271: 0x89, 0x272: 0x8a, 0x273: 0x8b, 0x274: 0x8c, 0x275: 0x8d, 0x276: 0x87, 0x277: 0x88, + 0x278: 0x89, 0x279: 0x8a, 0x27a: 0x8b, 0x27b: 0x8c, 0x27c: 0x8d, 0x27d: 0x87, 0x27e: 0x88, 0x27f: 0x89, + // Block 0xa, offset 0x280 + 0x280: 0x8a, 0x281: 0x8b, 0x282: 0x8c, 0x283: 0x8d, 0x284: 0x87, 0x285: 0x88, 0x286: 0x89, 0x287: 0x8a, + 0x288: 0x8b, 0x289: 0x8c, 0x28a: 0x8d, 0x28b: 0x87, 0x28c: 0x88, 0x28d: 0x89, 0x28e: 0x8a, 0x28f: 0x8b, + 0x290: 0x8c, 0x291: 0x8d, 0x292: 0x87, 0x293: 0x88, 0x294: 0x89, 0x295: 0x8a, 0x296: 0x8b, 0x297: 0x8c, + 0x298: 0x8d, 0x299: 0x87, 0x29a: 0x88, 0x29b: 0x89, 0x29c: 0x8a, 0x29d: 0x8b, 0x29e: 0x8c, 0x29f: 0x8d, + 0x2a0: 0x87, 0x2a1: 0x88, 0x2a2: 0x89, 0x2a3: 0x8a, 0x2a4: 0x8b, 0x2a5: 0x8c, 0x2a6: 0x8d, 0x2a7: 0x87, + 0x2a8: 0x88, 0x2a9: 0x89, 0x2aa: 0x8a, 0x2ab: 0x8b, 0x2ac: 0x8c, 0x2ad: 0x8d, 0x2ae: 0x87, 0x2af: 0x88, + 0x2b0: 0x89, 0x2b1: 0x8a, 0x2b2: 0x8b, 0x2b3: 0x8c, 0x2b4: 0x8d, 0x2b5: 0x87, 0x2b6: 0x88, 0x2b7: 0x89, + 0x2b8: 0x8a, 0x2b9: 0x8b, 0x2ba: 0x8c, 0x2bb: 0x8d, 0x2bc: 0x87, 0x2bd: 0x88, 0x2be: 0x89, 0x2bf: 0x8a, + // Block 0xb, offset 0x2c0 + 0x2c0: 0x8b, 0x2c1: 0x8c, 0x2c2: 0x8d, 0x2c3: 0x87, 0x2c4: 0x88, 0x2c5: 0x89, 0x2c6: 0x8a, 0x2c7: 0x8b, + 0x2c8: 0x8c, 0x2c9: 0x8d, 0x2ca: 0x87, 0x2cb: 0x88, 0x2cc: 0x89, 0x2cd: 0x8a, 0x2ce: 0x8b, 0x2cf: 0x8c, + 0x2d0: 0x8d, 0x2d1: 0x87, 0x2d2: 0x88, 0x2d3: 0x89, 0x2d4: 0x8a, 0x2d5: 0x8b, 0x2d6: 0x8c, 0x2d7: 0x8d, + 0x2d8: 0x87, 0x2d9: 0x88, 0x2da: 0x89, 0x2db: 0x8a, 0x2dc: 0x8b, 0x2dd: 0x8c, 0x2de: 0x8e, + // Block 0xc, offset 0x300 + 0x324: 0x1b, 0x325: 0x1c, 0x326: 0x1d, 0x327: 0x1e, + 0x328: 0x1f, 0x329: 0x20, 0x32a: 0x21, 0x32b: 0x22, 0x32c: 0x8f, 0x32d: 0x90, 0x32e: 0x91, + 0x331: 0x92, 0x332: 0x93, 0x333: 0x94, 0x334: 0x95, + 0x338: 0x96, 0x339: 0x97, 0x33a: 0x98, 0x33b: 0x99, 0x33e: 0x9a, 0x33f: 0x9b, + // Block 0xd, offset 0x340 + 0x347: 0x9c, + 0x34b: 0x9d, 0x34d: 0x9e, + 0x368: 0x9f, 0x36b: 0xa0, + // Block 0xe, offset 0x380 + 0x381: 0xa1, 0x382: 0xa2, 0x384: 0xa3, 0x385: 0x82, 0x387: 0xa4, + 0x388: 0xa5, 0x38b: 0xa6, 0x38c: 0x3e, 0x38d: 0xa7, + 0x392: 0xa8, 0x393: 0xa9, 0x396: 0xaa, 0x397: 0xab, + 0x398: 0x73, 0x39a: 0xac, 0x39c: 0xad, + // Block 0xf, offset 0x3c0 + 0x3eb: 0xae, 0x3ec: 0xaf, + // Block 0x10, offset 0x400 + 0x432: 0xb0, + // Block 0x11, offset 0x440 + 0x445: 0xb1, 0x446: 0xb2, 0x447: 0xb3, + 0x449: 0xb4, + // Block 0x12, offset 0x480 + 0x4a3: 0xb5, + // Block 0x13, offset 0x4c0 + 0x4c8: 0xb6, + // Block 0x14, offset 0x500 + 0x520: 0x23, 0x521: 0x24, 0x522: 0x25, 0x523: 0x26, 0x524: 0x27, 0x525: 0x28, 0x526: 0x29, 0x527: 0x2a, + 0x528: 0x2b, + // Block 0x15, offset 0x540 + 0x550: 0x0b, 0x551: 0x0c, 0x556: 0x0d, + 0x55b: 0x0e, 0x55d: 0x0f, 0x55e: 0x10, 0x55f: 0x11, + 0x56f: 0x12, +} + +// nfcSparseOffset: 139 entries, 278 bytes +var nfcSparseOffset = []uint16{0x0, 0x5, 0x9, 0xb, 0xd, 0x18, 0x28, 0x2a, 0x2f, 0x3a, 0x49, 0x56, 0x5e, 0x62, 0x67, 0x69, 0x79, 0x81, 0x88, 0x8b, 0x92, 0x96, 0x9a, 0x9c, 0x9e, 0xa7, 0xab, 0xb2, 0xb7, 0xba, 0xc4, 0xc6, 0xcd, 0xd5, 0xd8, 0xda, 0xdc, 0xde, 0xe3, 0xf4, 0x100, 0x102, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, 0x114, 0x117, 0x11a, 0x11c, 0x11f, 0x122, 0x126, 0x12b, 0x134, 0x136, 0x139, 0x13b, 0x146, 0x155, 0x159, 0x167, 0x16a, 0x170, 0x176, 0x181, 0x185, 0x187, 0x189, 0x18b, 0x18d, 0x18f, 0x195, 0x199, 0x19b, 0x19d, 0x1a5, 0x1a9, 0x1ac, 0x1ae, 0x1b0, 0x1b2, 0x1b5, 0x1b7, 0x1b9, 0x1bb, 0x1bd, 0x1c3, 0x1c6, 0x1c8, 0x1cf, 0x1d5, 0x1db, 0x1e3, 0x1e9, 0x1ef, 0x1f5, 0x1f9, 0x207, 0x210, 0x213, 0x216, 0x218, 0x21b, 0x21d, 0x221, 0x226, 0x228, 0x22a, 0x22f, 0x235, 0x237, 0x239, 0x23b, 0x241, 0x244, 0x247, 0x24f, 0x256, 0x259, 0x25c, 0x25e, 0x266, 0x26d, 0x270, 0x276, 0x278, 0x27b, 0x27d, 0x27f, 0x281, 0x283, 0x290, 0x29a, 0x29c, 0x29e, 0x2a0} + +// nfcSparseValues: 674 entries, 2696 bytes +var nfcSparseValues = [674]valueRange{ + // Block 0x0, offset 0x0 + {value: 0x0000, lo: 0x04}, + {value: 0xa100, lo: 0xa8, hi: 0xa8}, + {value: 0x8100, lo: 0xaf, hi: 0xaf}, + {value: 0x8100, lo: 0xb4, hi: 0xb4}, + {value: 0x8100, lo: 0xb8, hi: 0xb8}, + // Block 0x1, offset 0x5 + {value: 0x0091, lo: 0x03}, + {value: 0x4774, lo: 0xa0, hi: 0xa1}, + {value: 0x47a6, lo: 0xaf, hi: 0xb0}, + {value: 0xa000, lo: 0xb7, hi: 0xb7}, + // Block 0x2, offset 0x9 + {value: 0x0000, lo: 0x01}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + // Block 0x3, offset 0xb + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0x98, hi: 0x9d}, + // Block 0x4, offset 0xd + {value: 0x0006, lo: 0x0a}, + {value: 0xa000, lo: 0x81, hi: 0x81}, + {value: 0xa000, lo: 0x85, hi: 0x85}, + {value: 0xa000, lo: 0x89, hi: 0x89}, + {value: 0x48d2, lo: 0x8a, hi: 0x8a}, + {value: 0x48f0, lo: 0x8b, hi: 0x8b}, + {value: 0x36c3, lo: 0x8c, hi: 0x8c}, + {value: 0x36db, lo: 0x8d, hi: 0x8d}, + {value: 0x4908, lo: 0x8e, hi: 0x8e}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0x36f9, lo: 0x93, hi: 0x94}, + // Block 0x5, offset 0x18 + {value: 0x0000, lo: 0x0f}, + {value: 0xa000, lo: 0x83, hi: 0x83}, + {value: 0xa000, lo: 0x87, hi: 0x87}, + {value: 0xa000, lo: 0x8b, hi: 0x8b}, + {value: 0xa000, lo: 0x8d, hi: 0x8d}, + {value: 0x37a1, lo: 0x90, hi: 0x90}, + {value: 0x37ad, lo: 0x91, hi: 0x91}, + {value: 0x379b, lo: 0x93, hi: 0x93}, + {value: 0xa000, lo: 0x96, hi: 0x96}, + {value: 0x3813, lo: 0x97, hi: 0x97}, + {value: 0x37dd, lo: 0x9c, hi: 0x9c}, + {value: 0x37c5, lo: 0x9d, hi: 0x9d}, + {value: 0x37ef, lo: 0x9e, hi: 0x9e}, + {value: 0xa000, lo: 0xb4, hi: 0xb5}, + {value: 0x3819, lo: 0xb6, hi: 0xb6}, + {value: 0x381f, lo: 0xb7, hi: 0xb7}, + // Block 0x6, offset 0x28 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0x83, hi: 0x87}, + // Block 0x7, offset 0x2a + {value: 0x0001, lo: 0x04}, + {value: 0x8113, lo: 0x81, hi: 0x82}, + {value: 0x8132, lo: 0x84, hi: 0x84}, + {value: 0x812d, lo: 0x85, hi: 0x85}, + {value: 0x810d, lo: 0x87, hi: 0x87}, + // Block 0x8, offset 0x2f + {value: 0x0000, lo: 0x0a}, + {value: 0x8132, lo: 0x90, hi: 0x97}, + {value: 0x8119, lo: 0x98, hi: 0x98}, + {value: 0x811a, lo: 0x99, hi: 0x99}, + {value: 0x811b, lo: 0x9a, hi: 0x9a}, + {value: 0x383d, lo: 0xa2, hi: 0xa2}, + {value: 0x3843, lo: 0xa3, hi: 0xa3}, + {value: 0x384f, lo: 0xa4, hi: 0xa4}, + {value: 0x3849, lo: 0xa5, hi: 0xa5}, + {value: 0x3855, lo: 0xa6, hi: 0xa6}, + {value: 0xa000, lo: 0xa7, hi: 0xa7}, + // Block 0x9, offset 0x3a + {value: 0x0000, lo: 0x0e}, + {value: 0x3867, lo: 0x80, hi: 0x80}, + {value: 0xa000, lo: 0x81, hi: 0x81}, + {value: 0x385b, lo: 0x82, hi: 0x82}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0x3861, lo: 0x93, hi: 0x93}, + {value: 0xa000, lo: 0x95, hi: 0x95}, + {value: 0x8132, lo: 0x96, hi: 0x9c}, + {value: 0x8132, lo: 0x9f, hi: 0xa2}, + {value: 0x812d, lo: 0xa3, hi: 0xa3}, + {value: 0x8132, lo: 0xa4, hi: 0xa4}, + {value: 0x8132, lo: 0xa7, hi: 0xa8}, + {value: 0x812d, lo: 0xaa, hi: 0xaa}, + {value: 0x8132, lo: 0xab, hi: 0xac}, + {value: 0x812d, lo: 0xad, hi: 0xad}, + // Block 0xa, offset 0x49 + {value: 0x0000, lo: 0x0c}, + {value: 0x811f, lo: 0x91, hi: 0x91}, + {value: 0x8132, lo: 0xb0, hi: 0xb0}, + {value: 0x812d, lo: 0xb1, hi: 0xb1}, + {value: 0x8132, lo: 0xb2, hi: 0xb3}, + {value: 0x812d, lo: 0xb4, hi: 0xb4}, + {value: 0x8132, lo: 0xb5, hi: 0xb6}, + {value: 0x812d, lo: 0xb7, hi: 0xb9}, + {value: 0x8132, lo: 0xba, hi: 0xba}, + {value: 0x812d, lo: 0xbb, hi: 0xbc}, + {value: 0x8132, lo: 0xbd, hi: 0xbd}, + {value: 0x812d, lo: 0xbe, hi: 0xbe}, + {value: 0x8132, lo: 0xbf, hi: 0xbf}, + // Block 0xb, offset 0x56 + {value: 0x0005, lo: 0x07}, + {value: 0x8132, lo: 0x80, hi: 0x80}, + {value: 0x8132, lo: 0x81, hi: 0x81}, + {value: 0x812d, lo: 0x82, hi: 0x83}, + {value: 0x812d, lo: 0x84, hi: 0x85}, + {value: 0x812d, lo: 0x86, hi: 0x87}, + {value: 0x812d, lo: 0x88, hi: 0x89}, + {value: 0x8132, lo: 0x8a, hi: 0x8a}, + // Block 0xc, offset 0x5e + {value: 0x0000, lo: 0x03}, + {value: 0x8132, lo: 0xab, hi: 0xb1}, + {value: 0x812d, lo: 0xb2, hi: 0xb2}, + {value: 0x8132, lo: 0xb3, hi: 0xb3}, + // Block 0xd, offset 0x62 + {value: 0x0000, lo: 0x04}, + {value: 0x8132, lo: 0x96, hi: 0x99}, + {value: 0x8132, lo: 0x9b, hi: 0xa3}, + {value: 0x8132, lo: 0xa5, hi: 0xa7}, + {value: 0x8132, lo: 0xa9, hi: 0xad}, + // Block 0xe, offset 0x67 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0x99, hi: 0x9b}, + // Block 0xf, offset 0x69 + {value: 0x0000, lo: 0x0f}, + {value: 0x812d, lo: 0xa3, hi: 0xa3}, + {value: 0x8132, lo: 0xa4, hi: 0xa5}, + {value: 0x812d, lo: 0xa6, hi: 0xa6}, + {value: 0x8132, lo: 0xa7, hi: 0xa8}, + {value: 0x812d, lo: 0xa9, hi: 0xa9}, + {value: 0x8132, lo: 0xaa, hi: 0xac}, + {value: 0x812d, lo: 0xad, hi: 0xaf}, + {value: 0x8116, lo: 0xb0, hi: 0xb0}, + {value: 0x8117, lo: 0xb1, hi: 0xb1}, + {value: 0x8118, lo: 0xb2, hi: 0xb2}, + {value: 0x8132, lo: 0xb3, hi: 0xb5}, + {value: 0x812d, lo: 0xb6, hi: 0xb6}, + {value: 0x8132, lo: 0xb7, hi: 0xb8}, + {value: 0x812d, lo: 0xb9, hi: 0xba}, + {value: 0x8132, lo: 0xbb, hi: 0xbf}, + // Block 0x10, offset 0x79 + {value: 0x0000, lo: 0x07}, + {value: 0xa000, lo: 0xa8, hi: 0xa8}, + {value: 0x3ed4, lo: 0xa9, hi: 0xa9}, + {value: 0xa000, lo: 0xb0, hi: 0xb0}, + {value: 0x3edc, lo: 0xb1, hi: 0xb1}, + {value: 0xa000, lo: 0xb3, hi: 0xb3}, + {value: 0x3ee4, lo: 0xb4, hi: 0xb4}, + {value: 0x9902, lo: 0xbc, hi: 0xbc}, + // Block 0x11, offset 0x81 + {value: 0x0008, lo: 0x06}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x8132, lo: 0x91, hi: 0x91}, + {value: 0x812d, lo: 0x92, hi: 0x92}, + {value: 0x8132, lo: 0x93, hi: 0x93}, + {value: 0x8132, lo: 0x94, hi: 0x94}, + {value: 0x45ae, lo: 0x98, hi: 0x9f}, + // Block 0x12, offset 0x88 + {value: 0x0000, lo: 0x02}, + {value: 0x8102, lo: 0xbc, hi: 0xbc}, + {value: 0x9900, lo: 0xbe, hi: 0xbe}, + // Block 0x13, offset 0x8b + {value: 0x0008, lo: 0x06}, + {value: 0xa000, lo: 0x87, hi: 0x87}, + {value: 0x2c9a, lo: 0x8b, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x97, hi: 0x97}, + {value: 0x45ee, lo: 0x9c, hi: 0x9d}, + {value: 0x45fe, lo: 0x9f, hi: 0x9f}, + // Block 0x14, offset 0x92 + {value: 0x0000, lo: 0x03}, + {value: 0x4626, lo: 0xb3, hi: 0xb3}, + {value: 0x462e, lo: 0xb6, hi: 0xb6}, + {value: 0x8102, lo: 0xbc, hi: 0xbc}, + // Block 0x15, offset 0x96 + {value: 0x0008, lo: 0x03}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x4606, lo: 0x99, hi: 0x9b}, + {value: 0x461e, lo: 0x9e, hi: 0x9e}, + // Block 0x16, offset 0x9a + {value: 0x0000, lo: 0x01}, + {value: 0x8102, lo: 0xbc, hi: 0xbc}, + // Block 0x17, offset 0x9c + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + // Block 0x18, offset 0x9e + {value: 0x0000, lo: 0x08}, + {value: 0xa000, lo: 0x87, hi: 0x87}, + {value: 0x2cb2, lo: 0x88, hi: 0x88}, + {value: 0x2caa, lo: 0x8b, hi: 0x8b}, + {value: 0x2cba, lo: 0x8c, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x96, hi: 0x97}, + {value: 0x4636, lo: 0x9c, hi: 0x9c}, + {value: 0x463e, lo: 0x9d, hi: 0x9d}, + // Block 0x19, offset 0xa7 + {value: 0x0000, lo: 0x03}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0x2cc2, lo: 0x94, hi: 0x94}, + {value: 0x9900, lo: 0xbe, hi: 0xbe}, + // Block 0x1a, offset 0xab + {value: 0x0000, lo: 0x06}, + {value: 0xa000, lo: 0x86, hi: 0x87}, + {value: 0x2cca, lo: 0x8a, hi: 0x8a}, + {value: 0x2cda, lo: 0x8b, hi: 0x8b}, + {value: 0x2cd2, lo: 0x8c, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x97, hi: 0x97}, + // Block 0x1b, offset 0xb2 + {value: 0x1801, lo: 0x04}, + {value: 0xa000, lo: 0x86, hi: 0x86}, + {value: 0x3eec, lo: 0x88, hi: 0x88}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x8120, lo: 0x95, hi: 0x96}, + // Block 0x1c, offset 0xb7 + {value: 0x0000, lo: 0x02}, + {value: 0x8102, lo: 0xbc, hi: 0xbc}, + {value: 0xa000, lo: 0xbf, hi: 0xbf}, + // Block 0x1d, offset 0xba + {value: 0x0000, lo: 0x09}, + {value: 0x2ce2, lo: 0x80, hi: 0x80}, + {value: 0x9900, lo: 0x82, hi: 0x82}, + {value: 0xa000, lo: 0x86, hi: 0x86}, + {value: 0x2cea, lo: 0x87, hi: 0x87}, + {value: 0x2cf2, lo: 0x88, hi: 0x88}, + {value: 0x2f4c, lo: 0x8a, hi: 0x8a}, + {value: 0x2dd4, lo: 0x8b, hi: 0x8b}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x95, hi: 0x96}, + // Block 0x1e, offset 0xc4 + {value: 0x0000, lo: 0x01}, + {value: 0x9900, lo: 0xbe, hi: 0xbe}, + // Block 0x1f, offset 0xc6 + {value: 0x0000, lo: 0x06}, + {value: 0xa000, lo: 0x86, hi: 0x87}, + {value: 0x2cfa, lo: 0x8a, hi: 0x8a}, + {value: 0x2d0a, lo: 0x8b, hi: 0x8b}, + {value: 0x2d02, lo: 0x8c, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x97, hi: 0x97}, + // Block 0x20, offset 0xcd + {value: 0x6bee, lo: 0x07}, + {value: 0x9904, lo: 0x8a, hi: 0x8a}, + {value: 0x9900, lo: 0x8f, hi: 0x8f}, + {value: 0xa000, lo: 0x99, hi: 0x99}, + {value: 0x3ef4, lo: 0x9a, hi: 0x9a}, + {value: 0x2f54, lo: 0x9c, hi: 0x9c}, + {value: 0x2ddf, lo: 0x9d, hi: 0x9d}, + {value: 0x2d12, lo: 0x9e, hi: 0x9f}, + // Block 0x21, offset 0xd5 + {value: 0x0000, lo: 0x02}, + {value: 0x8122, lo: 0xb8, hi: 0xb9}, + {value: 0x8104, lo: 0xba, hi: 0xba}, + // Block 0x22, offset 0xd8 + {value: 0x0000, lo: 0x01}, + {value: 0x8123, lo: 0x88, hi: 0x8b}, + // Block 0x23, offset 0xda + {value: 0x0000, lo: 0x01}, + {value: 0x8124, lo: 0xb8, hi: 0xb9}, + // Block 0x24, offset 0xdc + {value: 0x0000, lo: 0x01}, + {value: 0x8125, lo: 0x88, hi: 0x8b}, + // Block 0x25, offset 0xde + {value: 0x0000, lo: 0x04}, + {value: 0x812d, lo: 0x98, hi: 0x99}, + {value: 0x812d, lo: 0xb5, hi: 0xb5}, + {value: 0x812d, lo: 0xb7, hi: 0xb7}, + {value: 0x812b, lo: 0xb9, hi: 0xb9}, + // Block 0x26, offset 0xe3 + {value: 0x0000, lo: 0x10}, + {value: 0x2640, lo: 0x83, hi: 0x83}, + {value: 0x2647, lo: 0x8d, hi: 0x8d}, + {value: 0x264e, lo: 0x92, hi: 0x92}, + {value: 0x2655, lo: 0x97, hi: 0x97}, + {value: 0x265c, lo: 0x9c, hi: 0x9c}, + {value: 0x2639, lo: 0xa9, hi: 0xa9}, + {value: 0x8126, lo: 0xb1, hi: 0xb1}, + {value: 0x8127, lo: 0xb2, hi: 0xb2}, + {value: 0x4a62, lo: 0xb3, hi: 0xb3}, + {value: 0x8128, lo: 0xb4, hi: 0xb4}, + {value: 0x4a6b, lo: 0xb5, hi: 0xb5}, + {value: 0x4646, lo: 0xb6, hi: 0xb6}, + {value: 0x8200, lo: 0xb7, hi: 0xb7}, + {value: 0x464e, lo: 0xb8, hi: 0xb8}, + {value: 0x8200, lo: 0xb9, hi: 0xb9}, + {value: 0x8127, lo: 0xba, hi: 0xbd}, + // Block 0x27, offset 0xf4 + {value: 0x0000, lo: 0x0b}, + {value: 0x8127, lo: 0x80, hi: 0x80}, + {value: 0x4a74, lo: 0x81, hi: 0x81}, + {value: 0x8132, lo: 0x82, hi: 0x83}, + {value: 0x8104, lo: 0x84, hi: 0x84}, + {value: 0x8132, lo: 0x86, hi: 0x87}, + {value: 0x266a, lo: 0x93, hi: 0x93}, + {value: 0x2671, lo: 0x9d, hi: 0x9d}, + {value: 0x2678, lo: 0xa2, hi: 0xa2}, + {value: 0x267f, lo: 0xa7, hi: 0xa7}, + {value: 0x2686, lo: 0xac, hi: 0xac}, + {value: 0x2663, lo: 0xb9, hi: 0xb9}, + // Block 0x28, offset 0x100 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0x86, hi: 0x86}, + // Block 0x29, offset 0x102 + {value: 0x0000, lo: 0x05}, + {value: 0xa000, lo: 0xa5, hi: 0xa5}, + {value: 0x2d1a, lo: 0xa6, hi: 0xa6}, + {value: 0x9900, lo: 0xae, hi: 0xae}, + {value: 0x8102, lo: 0xb7, hi: 0xb7}, + {value: 0x8104, lo: 0xb9, hi: 0xba}, + // Block 0x2a, offset 0x108 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0x8d, hi: 0x8d}, + // Block 0x2b, offset 0x10a + {value: 0x0000, lo: 0x01}, + {value: 0xa000, lo: 0x80, hi: 0x92}, + // Block 0x2c, offset 0x10c + {value: 0x0000, lo: 0x01}, + {value: 0xb900, lo: 0xa1, hi: 0xb5}, + // Block 0x2d, offset 0x10e + {value: 0x0000, lo: 0x01}, + {value: 0x9900, lo: 0xa8, hi: 0xbf}, + // Block 0x2e, offset 0x110 + {value: 0x0000, lo: 0x01}, + {value: 0x9900, lo: 0x80, hi: 0x82}, + // Block 0x2f, offset 0x112 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0x9d, hi: 0x9f}, + // Block 0x30, offset 0x114 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x94, hi: 0x94}, + {value: 0x8104, lo: 0xb4, hi: 0xb4}, + // Block 0x31, offset 0x117 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x92, hi: 0x92}, + {value: 0x8132, lo: 0x9d, hi: 0x9d}, + // Block 0x32, offset 0x11a + {value: 0x0000, lo: 0x01}, + {value: 0x8131, lo: 0xa9, hi: 0xa9}, + // Block 0x33, offset 0x11c + {value: 0x0004, lo: 0x02}, + {value: 0x812e, lo: 0xb9, hi: 0xba}, + {value: 0x812d, lo: 0xbb, hi: 0xbb}, + // Block 0x34, offset 0x11f + {value: 0x0000, lo: 0x02}, + {value: 0x8132, lo: 0x97, hi: 0x97}, + {value: 0x812d, lo: 0x98, hi: 0x98}, + // Block 0x35, offset 0x122 + {value: 0x0000, lo: 0x03}, + {value: 0x8104, lo: 0xa0, hi: 0xa0}, + {value: 0x8132, lo: 0xb5, hi: 0xbc}, + {value: 0x812d, lo: 0xbf, hi: 0xbf}, + // Block 0x36, offset 0x126 + {value: 0x0000, lo: 0x04}, + {value: 0x8132, lo: 0xb0, hi: 0xb4}, + {value: 0x812d, lo: 0xb5, hi: 0xba}, + {value: 0x8132, lo: 0xbb, hi: 0xbc}, + {value: 0x812d, lo: 0xbd, hi: 0xbd}, + // Block 0x37, offset 0x12b + {value: 0x0000, lo: 0x08}, + {value: 0x2d62, lo: 0x80, hi: 0x80}, + {value: 0x2d6a, lo: 0x81, hi: 0x81}, + {value: 0xa000, lo: 0x82, hi: 0x82}, + {value: 0x2d72, lo: 0x83, hi: 0x83}, + {value: 0x8104, lo: 0x84, hi: 0x84}, + {value: 0x8132, lo: 0xab, hi: 0xab}, + {value: 0x812d, lo: 0xac, hi: 0xac}, + {value: 0x8132, lo: 0xad, hi: 0xb3}, + // Block 0x38, offset 0x134 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0xaa, hi: 0xab}, + // Block 0x39, offset 0x136 + {value: 0x0000, lo: 0x02}, + {value: 0x8102, lo: 0xa6, hi: 0xa6}, + {value: 0x8104, lo: 0xb2, hi: 0xb3}, + // Block 0x3a, offset 0x139 + {value: 0x0000, lo: 0x01}, + {value: 0x8102, lo: 0xb7, hi: 0xb7}, + // Block 0x3b, offset 0x13b + {value: 0x0000, lo: 0x0a}, + {value: 0x8132, lo: 0x90, hi: 0x92}, + {value: 0x8101, lo: 0x94, hi: 0x94}, + {value: 0x812d, lo: 0x95, hi: 0x99}, + {value: 0x8132, lo: 0x9a, hi: 0x9b}, + {value: 0x812d, lo: 0x9c, hi: 0x9f}, + {value: 0x8132, lo: 0xa0, hi: 0xa0}, + {value: 0x8101, lo: 0xa2, hi: 0xa8}, + {value: 0x812d, lo: 0xad, hi: 0xad}, + {value: 0x8132, lo: 0xb4, hi: 0xb4}, + {value: 0x8132, lo: 0xb8, hi: 0xb9}, + // Block 0x3c, offset 0x146 + {value: 0x0000, lo: 0x0e}, + {value: 0x8132, lo: 0x80, hi: 0x81}, + {value: 0x812d, lo: 0x82, hi: 0x82}, + {value: 0x8132, lo: 0x83, hi: 0x89}, + {value: 0x812d, lo: 0x8a, hi: 0x8a}, + {value: 0x8132, lo: 0x8b, hi: 0x8c}, + {value: 0x8135, lo: 0x8d, hi: 0x8d}, + {value: 0x812a, lo: 0x8e, hi: 0x8e}, + {value: 0x812d, lo: 0x8f, hi: 0x8f}, + {value: 0x8129, lo: 0x90, hi: 0x90}, + {value: 0x8132, lo: 0x91, hi: 0xb5}, + {value: 0x8134, lo: 0xbc, hi: 0xbc}, + {value: 0x812d, lo: 0xbd, hi: 0xbd}, + {value: 0x8132, lo: 0xbe, hi: 0xbe}, + {value: 0x812d, lo: 0xbf, hi: 0xbf}, + // Block 0x3d, offset 0x155 + {value: 0x0004, lo: 0x03}, + {value: 0x0433, lo: 0x80, hi: 0x81}, + {value: 0x8100, lo: 0x97, hi: 0x97}, + {value: 0x8100, lo: 0xbe, hi: 0xbe}, + // Block 0x3e, offset 0x159 + {value: 0x0000, lo: 0x0d}, + {value: 0x8132, lo: 0x90, hi: 0x91}, + {value: 0x8101, lo: 0x92, hi: 0x93}, + {value: 0x8132, lo: 0x94, hi: 0x97}, + {value: 0x8101, lo: 0x98, hi: 0x9a}, + {value: 0x8132, lo: 0x9b, hi: 0x9c}, + {value: 0x8132, lo: 0xa1, hi: 0xa1}, + {value: 0x8101, lo: 0xa5, hi: 0xa6}, + {value: 0x8132, lo: 0xa7, hi: 0xa7}, + {value: 0x812d, lo: 0xa8, hi: 0xa8}, + {value: 0x8132, lo: 0xa9, hi: 0xa9}, + {value: 0x8101, lo: 0xaa, hi: 0xab}, + {value: 0x812d, lo: 0xac, hi: 0xaf}, + {value: 0x8132, lo: 0xb0, hi: 0xb0}, + // Block 0x3f, offset 0x167 + {value: 0x4277, lo: 0x02}, + {value: 0x01b8, lo: 0xa6, hi: 0xa6}, + {value: 0x0057, lo: 0xaa, hi: 0xab}, + // Block 0x40, offset 0x16a + {value: 0x0007, lo: 0x05}, + {value: 0xa000, lo: 0x90, hi: 0x90}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0xa000, lo: 0x94, hi: 0x94}, + {value: 0x3bb5, lo: 0x9a, hi: 0x9b}, + {value: 0x3bc3, lo: 0xae, hi: 0xae}, + // Block 0x41, offset 0x170 + {value: 0x000e, lo: 0x05}, + {value: 0x3bca, lo: 0x8d, hi: 0x8e}, + {value: 0x3bd1, lo: 0x8f, hi: 0x8f}, + {value: 0xa000, lo: 0x90, hi: 0x90}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0xa000, lo: 0x94, hi: 0x94}, + // Block 0x42, offset 0x176 + {value: 0x640c, lo: 0x0a}, + {value: 0xa000, lo: 0x83, hi: 0x83}, + {value: 0x3bdf, lo: 0x84, hi: 0x84}, + {value: 0xa000, lo: 0x88, hi: 0x88}, + {value: 0x3be6, lo: 0x89, hi: 0x89}, + {value: 0xa000, lo: 0x8b, hi: 0x8b}, + {value: 0x3bed, lo: 0x8c, hi: 0x8c}, + {value: 0xa000, lo: 0xa3, hi: 0xa3}, + {value: 0x3bf4, lo: 0xa4, hi: 0xa5}, + {value: 0x3bfb, lo: 0xa6, hi: 0xa6}, + {value: 0xa000, lo: 0xbc, hi: 0xbc}, + // Block 0x43, offset 0x181 + {value: 0x0007, lo: 0x03}, + {value: 0x3c64, lo: 0xa0, hi: 0xa1}, + {value: 0x3c8e, lo: 0xa2, hi: 0xa3}, + {value: 0x3cb8, lo: 0xaa, hi: 0xad}, + // Block 0x44, offset 0x185 + {value: 0x0004, lo: 0x01}, + {value: 0x048b, lo: 0xa9, hi: 0xaa}, + // Block 0x45, offset 0x187 + {value: 0x0000, lo: 0x01}, + {value: 0x456f, lo: 0x9c, hi: 0x9c}, + // Block 0x46, offset 0x189 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xaf, hi: 0xb1}, + // Block 0x47, offset 0x18b + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0xbf, hi: 0xbf}, + // Block 0x48, offset 0x18d + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xa0, hi: 0xbf}, + // Block 0x49, offset 0x18f + {value: 0x0000, lo: 0x05}, + {value: 0x812c, lo: 0xaa, hi: 0xaa}, + {value: 0x8131, lo: 0xab, hi: 0xab}, + {value: 0x8133, lo: 0xac, hi: 0xac}, + {value: 0x812e, lo: 0xad, hi: 0xad}, + {value: 0x812f, lo: 0xae, hi: 0xaf}, + // Block 0x4a, offset 0x195 + {value: 0x0000, lo: 0x03}, + {value: 0x4a7d, lo: 0xb3, hi: 0xb3}, + {value: 0x4a7d, lo: 0xb5, hi: 0xb6}, + {value: 0x4a7d, lo: 0xba, hi: 0xbf}, + // Block 0x4b, offset 0x199 + {value: 0x0000, lo: 0x01}, + {value: 0x4a7d, lo: 0x8f, hi: 0xa3}, + // Block 0x4c, offset 0x19b + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0xae, hi: 0xbe}, + // Block 0x4d, offset 0x19d + {value: 0x0000, lo: 0x07}, + {value: 0x8100, lo: 0x84, hi: 0x84}, + {value: 0x8100, lo: 0x87, hi: 0x87}, + {value: 0x8100, lo: 0x90, hi: 0x90}, + {value: 0x8100, lo: 0x9e, hi: 0x9e}, + {value: 0x8100, lo: 0xa1, hi: 0xa1}, + {value: 0x8100, lo: 0xb2, hi: 0xb2}, + {value: 0x8100, lo: 0xbb, hi: 0xbb}, + // Block 0x4e, offset 0x1a5 + {value: 0x0000, lo: 0x03}, + {value: 0x8100, lo: 0x80, hi: 0x80}, + {value: 0x8100, lo: 0x8b, hi: 0x8b}, + {value: 0x8100, lo: 0x8e, hi: 0x8e}, + // Block 0x4f, offset 0x1a9 + {value: 0x0000, lo: 0x02}, + {value: 0x8132, lo: 0xaf, hi: 0xaf}, + {value: 0x8132, lo: 0xb4, hi: 0xbd}, + // Block 0x50, offset 0x1ac + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0x9e, hi: 0x9f}, + // Block 0x51, offset 0x1ae + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xb0, hi: 0xb1}, + // Block 0x52, offset 0x1b0 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0x86, hi: 0x86}, + // Block 0x53, offset 0x1b2 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x84, hi: 0x84}, + {value: 0x8132, lo: 0xa0, hi: 0xb1}, + // Block 0x54, offset 0x1b5 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0xab, hi: 0xad}, + // Block 0x55, offset 0x1b7 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0x93, hi: 0x93}, + // Block 0x56, offset 0x1b9 + {value: 0x0000, lo: 0x01}, + {value: 0x8102, lo: 0xb3, hi: 0xb3}, + // Block 0x57, offset 0x1bb + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0x80, hi: 0x80}, + // Block 0x58, offset 0x1bd + {value: 0x0000, lo: 0x05}, + {value: 0x8132, lo: 0xb0, hi: 0xb0}, + {value: 0x8132, lo: 0xb2, hi: 0xb3}, + {value: 0x812d, lo: 0xb4, hi: 0xb4}, + {value: 0x8132, lo: 0xb7, hi: 0xb8}, + {value: 0x8132, lo: 0xbe, hi: 0xbf}, + // Block 0x59, offset 0x1c3 + {value: 0x0000, lo: 0x02}, + {value: 0x8132, lo: 0x81, hi: 0x81}, + {value: 0x8104, lo: 0xb6, hi: 0xb6}, + // Block 0x5a, offset 0x1c6 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0xad, hi: 0xad}, + // Block 0x5b, offset 0x1c8 + {value: 0x0000, lo: 0x06}, + {value: 0xe500, lo: 0x80, hi: 0x80}, + {value: 0xc600, lo: 0x81, hi: 0x9b}, + {value: 0xe500, lo: 0x9c, hi: 0x9c}, + {value: 0xc600, lo: 0x9d, hi: 0xb7}, + {value: 0xe500, lo: 0xb8, hi: 0xb8}, + {value: 0xc600, lo: 0xb9, hi: 0xbf}, + // Block 0x5c, offset 0x1cf + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x93}, + {value: 0xe500, lo: 0x94, hi: 0x94}, + {value: 0xc600, lo: 0x95, hi: 0xaf}, + {value: 0xe500, lo: 0xb0, hi: 0xb0}, + {value: 0xc600, lo: 0xb1, hi: 0xbf}, + // Block 0x5d, offset 0x1d5 + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x8b}, + {value: 0xe500, lo: 0x8c, hi: 0x8c}, + {value: 0xc600, lo: 0x8d, hi: 0xa7}, + {value: 0xe500, lo: 0xa8, hi: 0xa8}, + {value: 0xc600, lo: 0xa9, hi: 0xbf}, + // Block 0x5e, offset 0x1db + {value: 0x0000, lo: 0x07}, + {value: 0xc600, lo: 0x80, hi: 0x83}, + {value: 0xe500, lo: 0x84, hi: 0x84}, + {value: 0xc600, lo: 0x85, hi: 0x9f}, + {value: 0xe500, lo: 0xa0, hi: 0xa0}, + {value: 0xc600, lo: 0xa1, hi: 0xbb}, + {value: 0xe500, lo: 0xbc, hi: 0xbc}, + {value: 0xc600, lo: 0xbd, hi: 0xbf}, + // Block 0x5f, offset 0x1e3 + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x97}, + {value: 0xe500, lo: 0x98, hi: 0x98}, + {value: 0xc600, lo: 0x99, hi: 0xb3}, + {value: 0xe500, lo: 0xb4, hi: 0xb4}, + {value: 0xc600, lo: 0xb5, hi: 0xbf}, + // Block 0x60, offset 0x1e9 + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x8f}, + {value: 0xe500, lo: 0x90, hi: 0x90}, + {value: 0xc600, lo: 0x91, hi: 0xab}, + {value: 0xe500, lo: 0xac, hi: 0xac}, + {value: 0xc600, lo: 0xad, hi: 0xbf}, + // Block 0x61, offset 0x1ef + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x87}, + {value: 0xe500, lo: 0x88, hi: 0x88}, + {value: 0xc600, lo: 0x89, hi: 0xa3}, + {value: 0xe500, lo: 0xa4, hi: 0xa4}, + {value: 0xc600, lo: 0xa5, hi: 0xbf}, + // Block 0x62, offset 0x1f5 + {value: 0x0000, lo: 0x03}, + {value: 0xc600, lo: 0x80, hi: 0x87}, + {value: 0xe500, lo: 0x88, hi: 0x88}, + {value: 0xc600, lo: 0x89, hi: 0xa3}, + // Block 0x63, offset 0x1f9 + {value: 0x0006, lo: 0x0d}, + {value: 0x4422, lo: 0x9d, hi: 0x9d}, + {value: 0x8115, lo: 0x9e, hi: 0x9e}, + {value: 0x4494, lo: 0x9f, hi: 0x9f}, + {value: 0x4482, lo: 0xaa, hi: 0xab}, + {value: 0x4586, lo: 0xac, hi: 0xac}, + {value: 0x458e, lo: 0xad, hi: 0xad}, + {value: 0x43da, lo: 0xae, hi: 0xb1}, + {value: 0x43f8, lo: 0xb2, hi: 0xb4}, + {value: 0x4410, lo: 0xb5, hi: 0xb6}, + {value: 0x441c, lo: 0xb8, hi: 0xb8}, + {value: 0x4428, lo: 0xb9, hi: 0xbb}, + {value: 0x4440, lo: 0xbc, hi: 0xbc}, + {value: 0x4446, lo: 0xbe, hi: 0xbe}, + // Block 0x64, offset 0x207 + {value: 0x0006, lo: 0x08}, + {value: 0x444c, lo: 0x80, hi: 0x81}, + {value: 0x4458, lo: 0x83, hi: 0x84}, + {value: 0x446a, lo: 0x86, hi: 0x89}, + {value: 0x448e, lo: 0x8a, hi: 0x8a}, + {value: 0x440a, lo: 0x8b, hi: 0x8b}, + {value: 0x43f2, lo: 0x8c, hi: 0x8c}, + {value: 0x443a, lo: 0x8d, hi: 0x8d}, + {value: 0x4464, lo: 0x8e, hi: 0x8e}, + // Block 0x65, offset 0x210 + {value: 0x0000, lo: 0x02}, + {value: 0x8100, lo: 0xa4, hi: 0xa5}, + {value: 0x8100, lo: 0xb0, hi: 0xb1}, + // Block 0x66, offset 0x213 + {value: 0x0000, lo: 0x02}, + {value: 0x8100, lo: 0x9b, hi: 0x9d}, + {value: 0x8200, lo: 0x9e, hi: 0xa3}, + // Block 0x67, offset 0x216 + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0x90, hi: 0x90}, + // Block 0x68, offset 0x218 + {value: 0x0000, lo: 0x02}, + {value: 0x8100, lo: 0x99, hi: 0x99}, + {value: 0x8200, lo: 0xb2, hi: 0xb4}, + // Block 0x69, offset 0x21b + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0xbc, hi: 0xbd}, + // Block 0x6a, offset 0x21d + {value: 0x0000, lo: 0x03}, + {value: 0x8132, lo: 0xa0, hi: 0xa6}, + {value: 0x812d, lo: 0xa7, hi: 0xad}, + {value: 0x8132, lo: 0xae, hi: 0xaf}, + // Block 0x6b, offset 0x221 + {value: 0x0000, lo: 0x04}, + {value: 0x8100, lo: 0x89, hi: 0x8c}, + {value: 0x8100, lo: 0xb0, hi: 0xb2}, + {value: 0x8100, lo: 0xb4, hi: 0xb4}, + {value: 0x8100, lo: 0xb6, hi: 0xbf}, + // Block 0x6c, offset 0x226 + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0x81, hi: 0x8c}, + // Block 0x6d, offset 0x228 + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0xb5, hi: 0xba}, + // Block 0x6e, offset 0x22a + {value: 0x0000, lo: 0x04}, + {value: 0x4a7d, lo: 0x9e, hi: 0x9f}, + {value: 0x4a7d, lo: 0xa3, hi: 0xa3}, + {value: 0x4a7d, lo: 0xa5, hi: 0xa6}, + {value: 0x4a7d, lo: 0xaa, hi: 0xaf}, + // Block 0x6f, offset 0x22f + {value: 0x0000, lo: 0x05}, + {value: 0x4a7d, lo: 0x82, hi: 0x87}, + {value: 0x4a7d, lo: 0x8a, hi: 0x8f}, + {value: 0x4a7d, lo: 0x92, hi: 0x97}, + {value: 0x4a7d, lo: 0x9a, hi: 0x9c}, + {value: 0x8100, lo: 0xa3, hi: 0xa3}, + // Block 0x70, offset 0x235 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0xbd, hi: 0xbd}, + // Block 0x71, offset 0x237 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0xa0, hi: 0xa0}, + // Block 0x72, offset 0x239 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xb6, hi: 0xba}, + // Block 0x73, offset 0x23b + {value: 0x002c, lo: 0x05}, + {value: 0x812d, lo: 0x8d, hi: 0x8d}, + {value: 0x8132, lo: 0x8f, hi: 0x8f}, + {value: 0x8132, lo: 0xb8, hi: 0xb8}, + {value: 0x8101, lo: 0xb9, hi: 0xba}, + {value: 0x8104, lo: 0xbf, hi: 0xbf}, + // Block 0x74, offset 0x241 + {value: 0x0000, lo: 0x02}, + {value: 0x8132, lo: 0xa5, hi: 0xa5}, + {value: 0x812d, lo: 0xa6, hi: 0xa6}, + // Block 0x75, offset 0x244 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x86, hi: 0x86}, + {value: 0x8104, lo: 0xbf, hi: 0xbf}, + // Block 0x76, offset 0x247 + {value: 0x17fe, lo: 0x07}, + {value: 0xa000, lo: 0x99, hi: 0x99}, + {value: 0x4234, lo: 0x9a, hi: 0x9a}, + {value: 0xa000, lo: 0x9b, hi: 0x9b}, + {value: 0x423e, lo: 0x9c, hi: 0x9c}, + {value: 0xa000, lo: 0xa5, hi: 0xa5}, + {value: 0x4248, lo: 0xab, hi: 0xab}, + {value: 0x8104, lo: 0xb9, hi: 0xba}, + // Block 0x77, offset 0x24f + {value: 0x0000, lo: 0x06}, + {value: 0x8132, lo: 0x80, hi: 0x82}, + {value: 0x9900, lo: 0xa7, hi: 0xa7}, + {value: 0x2d7a, lo: 0xae, hi: 0xae}, + {value: 0x2d84, lo: 0xaf, hi: 0xaf}, + {value: 0xa000, lo: 0xb1, hi: 0xb2}, + {value: 0x8104, lo: 0xb3, hi: 0xb4}, + // Block 0x78, offset 0x256 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x80, hi: 0x80}, + {value: 0x8102, lo: 0x8a, hi: 0x8a}, + // Block 0x79, offset 0x259 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0xb5, hi: 0xb5}, + {value: 0x8102, lo: 0xb6, hi: 0xb6}, + // Block 0x7a, offset 0x25c + {value: 0x0002, lo: 0x01}, + {value: 0x8102, lo: 0xa9, hi: 0xaa}, + // Block 0x7b, offset 0x25e + {value: 0x0000, lo: 0x07}, + {value: 0xa000, lo: 0x87, hi: 0x87}, + {value: 0x2d8e, lo: 0x8b, hi: 0x8b}, + {value: 0x2d98, lo: 0x8c, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x97, hi: 0x97}, + {value: 0x8132, lo: 0xa6, hi: 0xac}, + {value: 0x8132, lo: 0xb0, hi: 0xb4}, + // Block 0x7c, offset 0x266 + {value: 0x6b5e, lo: 0x06}, + {value: 0x9900, lo: 0xb0, hi: 0xb0}, + {value: 0xa000, lo: 0xb9, hi: 0xb9}, + {value: 0x9900, lo: 0xba, hi: 0xba}, + {value: 0x2dac, lo: 0xbb, hi: 0xbb}, + {value: 0x2da2, lo: 0xbc, hi: 0xbd}, + {value: 0x2db6, lo: 0xbe, hi: 0xbe}, + // Block 0x7d, offset 0x26d + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x82, hi: 0x82}, + {value: 0x8102, lo: 0x83, hi: 0x83}, + // Block 0x7e, offset 0x270 + {value: 0x0000, lo: 0x05}, + {value: 0x9900, lo: 0xaf, hi: 0xaf}, + {value: 0xa000, lo: 0xb8, hi: 0xb9}, + {value: 0x2dc0, lo: 0xba, hi: 0xba}, + {value: 0x2dca, lo: 0xbb, hi: 0xbb}, + {value: 0x8104, lo: 0xbf, hi: 0xbf}, + // Block 0x7f, offset 0x276 + {value: 0x0000, lo: 0x01}, + {value: 0x8102, lo: 0x80, hi: 0x80}, + // Block 0x80, offset 0x278 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0xb6, hi: 0xb6}, + {value: 0x8102, lo: 0xb7, hi: 0xb7}, + // Block 0x81, offset 0x27b + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0xab, hi: 0xab}, + // Block 0x82, offset 0x27d + {value: 0x0000, lo: 0x01}, + {value: 0x8101, lo: 0xb0, hi: 0xb4}, + // Block 0x83, offset 0x27f + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xb0, hi: 0xb6}, + // Block 0x84, offset 0x281 + {value: 0x0000, lo: 0x01}, + {value: 0x8101, lo: 0x9e, hi: 0x9e}, + // Block 0x85, offset 0x283 + {value: 0x0000, lo: 0x0c}, + {value: 0x465e, lo: 0x9e, hi: 0x9e}, + {value: 0x4668, lo: 0x9f, hi: 0x9f}, + {value: 0x469c, lo: 0xa0, hi: 0xa0}, + {value: 0x46aa, lo: 0xa1, hi: 0xa1}, + {value: 0x46b8, lo: 0xa2, hi: 0xa2}, + {value: 0x46c6, lo: 0xa3, hi: 0xa3}, + {value: 0x46d4, lo: 0xa4, hi: 0xa4}, + {value: 0x812b, lo: 0xa5, hi: 0xa6}, + {value: 0x8101, lo: 0xa7, hi: 0xa9}, + {value: 0x8130, lo: 0xad, hi: 0xad}, + {value: 0x812b, lo: 0xae, hi: 0xb2}, + {value: 0x812d, lo: 0xbb, hi: 0xbf}, + // Block 0x86, offset 0x290 + {value: 0x0000, lo: 0x09}, + {value: 0x812d, lo: 0x80, hi: 0x82}, + {value: 0x8132, lo: 0x85, hi: 0x89}, + {value: 0x812d, lo: 0x8a, hi: 0x8b}, + {value: 0x8132, lo: 0xaa, hi: 0xad}, + {value: 0x4672, lo: 0xbb, hi: 0xbb}, + {value: 0x467c, lo: 0xbc, hi: 0xbc}, + {value: 0x46e2, lo: 0xbd, hi: 0xbd}, + {value: 0x46fe, lo: 0xbe, hi: 0xbe}, + {value: 0x46f0, lo: 0xbf, hi: 0xbf}, + // Block 0x87, offset 0x29a + {value: 0x0000, lo: 0x01}, + {value: 0x470c, lo: 0x80, hi: 0x80}, + // Block 0x88, offset 0x29c + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0x82, hi: 0x84}, + // Block 0x89, offset 0x29e + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0x90, hi: 0x96}, + // Block 0x8a, offset 0x2a0 + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0x93, hi: 0x93}, +} + +// lookup returns the trie value for the first UTF-8 encoding in s and +// the width in bytes of this encoding. The size will be 0 if s does not +// hold enough bytes to complete the encoding. len(s) must be greater than 0. +func (t *nfkcTrie) lookup(s []byte) (v uint16, sz int) { + c0 := s[0] + switch { + case c0 < 0x80: // is ASCII + return nfkcValues[c0], 1 + case c0 < 0xC0: + return 0, 1 // Illegal UTF-8: not a starter, not ASCII. + case c0 < 0xE0: // 2-byte UTF-8 + if len(s) < 2 { + return 0, 0 + } + i := nfkcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c1), 2 + case c0 < 0xF0: // 3-byte UTF-8 + if len(s) < 3 { + return 0, 0 + } + i := nfkcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = nfkcIndex[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c2), 3 + case c0 < 0xF8: // 4-byte UTF-8 + if len(s) < 4 { + return 0, 0 + } + i := nfkcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = nfkcIndex[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + o = uint32(i)<<6 + uint32(c2) + i = nfkcIndex[o] + c3 := s[3] + if c3 < 0x80 || 0xC0 <= c3 { + return 0, 3 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c3), 4 + } + // Illegal rune + return 0, 1 +} + +// lookupUnsafe returns the trie value for the first UTF-8 encoding in s. +// s must start with a full and valid UTF-8 encoded rune. +func (t *nfkcTrie) lookupUnsafe(s []byte) uint16 { + c0 := s[0] + if c0 < 0x80 { // is ASCII + return nfkcValues[c0] + } + i := nfkcIndex[c0] + if c0 < 0xE0 { // 2-byte UTF-8 + return t.lookupValue(uint32(i), s[1]) + } + i = nfkcIndex[uint32(i)<<6+uint32(s[1])] + if c0 < 0xF0 { // 3-byte UTF-8 + return t.lookupValue(uint32(i), s[2]) + } + i = nfkcIndex[uint32(i)<<6+uint32(s[2])] + if c0 < 0xF8 { // 4-byte UTF-8 + return t.lookupValue(uint32(i), s[3]) + } + return 0 +} + +// lookupString returns the trie value for the first UTF-8 encoding in s and +// the width in bytes of this encoding. The size will be 0 if s does not +// hold enough bytes to complete the encoding. len(s) must be greater than 0. +func (t *nfkcTrie) lookupString(s string) (v uint16, sz int) { + c0 := s[0] + switch { + case c0 < 0x80: // is ASCII + return nfkcValues[c0], 1 + case c0 < 0xC0: + return 0, 1 // Illegal UTF-8: not a starter, not ASCII. + case c0 < 0xE0: // 2-byte UTF-8 + if len(s) < 2 { + return 0, 0 + } + i := nfkcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c1), 2 + case c0 < 0xF0: // 3-byte UTF-8 + if len(s) < 3 { + return 0, 0 + } + i := nfkcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = nfkcIndex[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c2), 3 + case c0 < 0xF8: // 4-byte UTF-8 + if len(s) < 4 { + return 0, 0 + } + i := nfkcIndex[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = nfkcIndex[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + o = uint32(i)<<6 + uint32(c2) + i = nfkcIndex[o] + c3 := s[3] + if c3 < 0x80 || 0xC0 <= c3 { + return 0, 3 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c3), 4 + } + // Illegal rune + return 0, 1 +} + +// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s. +// s must start with a full and valid UTF-8 encoded rune. +func (t *nfkcTrie) lookupStringUnsafe(s string) uint16 { + c0 := s[0] + if c0 < 0x80 { // is ASCII + return nfkcValues[c0] + } + i := nfkcIndex[c0] + if c0 < 0xE0 { // 2-byte UTF-8 + return t.lookupValue(uint32(i), s[1]) + } + i = nfkcIndex[uint32(i)<<6+uint32(s[1])] + if c0 < 0xF0 { // 3-byte UTF-8 + return t.lookupValue(uint32(i), s[2]) + } + i = nfkcIndex[uint32(i)<<6+uint32(s[2])] + if c0 < 0xF8 { // 4-byte UTF-8 + return t.lookupValue(uint32(i), s[3]) + } + return 0 +} + +// nfkcTrie. Total size: 16932 bytes (16.54 KiB). Checksum: 30d032c1800b8f3e. +type nfkcTrie struct{} + +func newNfkcTrie(i int) *nfkcTrie { + return &nfkcTrie{} +} + +// lookupValue determines the type of block n and looks up the value for b. +func (t *nfkcTrie) lookupValue(n uint32, b byte) uint16 { + switch { + case n < 90: + return uint16(nfkcValues[n<<6+uint32(b)]) + default: + n -= 90 + return uint16(nfkcSparse.lookup(n, b)) + } +} + +// nfkcValues: 92 blocks, 5888 entries, 11776 bytes +// The third block is the zero block. +var nfkcValues = [5888]uint16{ + // Block 0x0, offset 0x0 + 0x3c: 0xa000, 0x3d: 0xa000, 0x3e: 0xa000, + // Block 0x1, offset 0x40 + 0x41: 0xa000, 0x42: 0xa000, 0x43: 0xa000, 0x44: 0xa000, 0x45: 0xa000, + 0x46: 0xa000, 0x47: 0xa000, 0x48: 0xa000, 0x49: 0xa000, 0x4a: 0xa000, 0x4b: 0xa000, + 0x4c: 0xa000, 0x4d: 0xa000, 0x4e: 0xa000, 0x4f: 0xa000, 0x50: 0xa000, + 0x52: 0xa000, 0x53: 0xa000, 0x54: 0xa000, 0x55: 0xa000, 0x56: 0xa000, 0x57: 0xa000, + 0x58: 0xa000, 0x59: 0xa000, 0x5a: 0xa000, + 0x61: 0xa000, 0x62: 0xa000, 0x63: 0xa000, + 0x64: 0xa000, 0x65: 0xa000, 0x66: 0xa000, 0x67: 0xa000, 0x68: 0xa000, 0x69: 0xa000, + 0x6a: 0xa000, 0x6b: 0xa000, 0x6c: 0xa000, 0x6d: 0xa000, 0x6e: 0xa000, 0x6f: 0xa000, + 0x70: 0xa000, 0x72: 0xa000, 0x73: 0xa000, 0x74: 0xa000, 0x75: 0xa000, + 0x76: 0xa000, 0x77: 0xa000, 0x78: 0xa000, 0x79: 0xa000, 0x7a: 0xa000, + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0xc0: 0x2f6b, 0xc1: 0x2f70, 0xc2: 0x471a, 0xc3: 0x2f75, 0xc4: 0x4729, 0xc5: 0x472e, + 0xc6: 0xa000, 0xc7: 0x4738, 0xc8: 0x2fde, 0xc9: 0x2fe3, 0xca: 0x473d, 0xcb: 0x2ff7, + 0xcc: 0x306a, 0xcd: 0x306f, 0xce: 0x3074, 0xcf: 0x4751, 0xd1: 0x3100, + 0xd2: 0x3123, 0xd3: 0x3128, 0xd4: 0x475b, 0xd5: 0x4760, 0xd6: 0x476f, + 0xd8: 0xa000, 0xd9: 0x31af, 0xda: 0x31b4, 0xdb: 0x31b9, 0xdc: 0x47a1, 0xdd: 0x3231, + 0xe0: 0x3277, 0xe1: 0x327c, 0xe2: 0x47ab, 0xe3: 0x3281, + 0xe4: 0x47ba, 0xe5: 0x47bf, 0xe6: 0xa000, 0xe7: 0x47c9, 0xe8: 0x32ea, 0xe9: 0x32ef, + 0xea: 0x47ce, 0xeb: 0x3303, 0xec: 0x337b, 0xed: 0x3380, 0xee: 0x3385, 0xef: 0x47e2, + 0xf1: 0x3411, 0xf2: 0x3434, 0xf3: 0x3439, 0xf4: 0x47ec, 0xf5: 0x47f1, + 0xf6: 0x4800, 0xf8: 0xa000, 0xf9: 0x34c5, 0xfa: 0x34ca, 0xfb: 0x34cf, + 0xfc: 0x4832, 0xfd: 0x354c, 0xff: 0x3565, + // Block 0x4, offset 0x100 + 0x100: 0x2f7a, 0x101: 0x3286, 0x102: 0x471f, 0x103: 0x47b0, 0x104: 0x2f98, 0x105: 0x32a4, + 0x106: 0x2fac, 0x107: 0x32b8, 0x108: 0x2fb1, 0x109: 0x32bd, 0x10a: 0x2fb6, 0x10b: 0x32c2, + 0x10c: 0x2fbb, 0x10d: 0x32c7, 0x10e: 0x2fc5, 0x10f: 0x32d1, + 0x112: 0x4742, 0x113: 0x47d3, 0x114: 0x2fed, 0x115: 0x32f9, 0x116: 0x2ff2, 0x117: 0x32fe, + 0x118: 0x3010, 0x119: 0x331c, 0x11a: 0x3001, 0x11b: 0x330d, 0x11c: 0x3029, 0x11d: 0x3335, + 0x11e: 0x3033, 0x11f: 0x333f, 0x120: 0x3038, 0x121: 0x3344, 0x122: 0x3042, 0x123: 0x334e, + 0x124: 0x3047, 0x125: 0x3353, 0x128: 0x3079, 0x129: 0x338a, + 0x12a: 0x307e, 0x12b: 0x338f, 0x12c: 0x3083, 0x12d: 0x3394, 0x12e: 0x30a6, 0x12f: 0x33b2, + 0x130: 0x3088, 0x132: 0x1959, 0x133: 0x19e3, 0x134: 0x30b0, 0x135: 0x33bc, + 0x136: 0x30c4, 0x137: 0x33d5, 0x139: 0x30ce, 0x13a: 0x33df, 0x13b: 0x30d8, + 0x13c: 0x33e9, 0x13d: 0x30d3, 0x13e: 0x33e4, 0x13f: 0x1ba8, + // Block 0x5, offset 0x140 + 0x140: 0x1c30, 0x143: 0x30fb, 0x144: 0x340c, 0x145: 0x3114, + 0x146: 0x3425, 0x147: 0x310a, 0x148: 0x341b, 0x149: 0x1c58, + 0x14c: 0x4765, 0x14d: 0x47f6, 0x14e: 0x312d, 0x14f: 0x343e, 0x150: 0x3137, 0x151: 0x3448, + 0x154: 0x3155, 0x155: 0x3466, 0x156: 0x316e, 0x157: 0x347f, + 0x158: 0x315f, 0x159: 0x3470, 0x15a: 0x4788, 0x15b: 0x4819, 0x15c: 0x3178, 0x15d: 0x3489, + 0x15e: 0x3187, 0x15f: 0x3498, 0x160: 0x478d, 0x161: 0x481e, 0x162: 0x31a0, 0x163: 0x34b6, + 0x164: 0x3191, 0x165: 0x34a7, 0x168: 0x4797, 0x169: 0x4828, + 0x16a: 0x479c, 0x16b: 0x482d, 0x16c: 0x31be, 0x16d: 0x34d4, 0x16e: 0x31c8, 0x16f: 0x34de, + 0x170: 0x31cd, 0x171: 0x34e3, 0x172: 0x31eb, 0x173: 0x3501, 0x174: 0x320e, 0x175: 0x3524, + 0x176: 0x3236, 0x177: 0x3551, 0x178: 0x324a, 0x179: 0x3259, 0x17a: 0x3579, 0x17b: 0x3263, + 0x17c: 0x3583, 0x17d: 0x3268, 0x17e: 0x3588, 0x17f: 0x00a7, + // Block 0x6, offset 0x180 + 0x184: 0x2dea, 0x185: 0x2df0, + 0x186: 0x2df6, 0x187: 0x196e, 0x188: 0x1971, 0x189: 0x1a04, 0x18a: 0x1983, 0x18b: 0x1986, + 0x18c: 0x1a3a, 0x18d: 0x2f84, 0x18e: 0x3290, 0x18f: 0x3092, 0x190: 0x339e, 0x191: 0x313c, + 0x192: 0x344d, 0x193: 0x31d2, 0x194: 0x34e8, 0x195: 0x39cb, 0x196: 0x3b5a, 0x197: 0x39c4, + 0x198: 0x3b53, 0x199: 0x39d2, 0x19a: 0x3b61, 0x19b: 0x39bd, 0x19c: 0x3b4c, + 0x19e: 0x38ac, 0x19f: 0x3a3b, 0x1a0: 0x38a5, 0x1a1: 0x3a34, 0x1a2: 0x35af, 0x1a3: 0x35c1, + 0x1a6: 0x303d, 0x1a7: 0x3349, 0x1a8: 0x30ba, 0x1a9: 0x33cb, + 0x1aa: 0x477e, 0x1ab: 0x480f, 0x1ac: 0x398c, 0x1ad: 0x3b1b, 0x1ae: 0x35d3, 0x1af: 0x35d9, + 0x1b0: 0x33c1, 0x1b1: 0x193e, 0x1b2: 0x1941, 0x1b3: 0x19cb, 0x1b4: 0x3024, 0x1b5: 0x3330, + 0x1b8: 0x30f6, 0x1b9: 0x3407, 0x1ba: 0x38b3, 0x1bb: 0x3a42, + 0x1bc: 0x35a9, 0x1bd: 0x35bb, 0x1be: 0x35b5, 0x1bf: 0x35c7, + // Block 0x7, offset 0x1c0 + 0x1c0: 0x2f89, 0x1c1: 0x3295, 0x1c2: 0x2f8e, 0x1c3: 0x329a, 0x1c4: 0x3006, 0x1c5: 0x3312, + 0x1c6: 0x300b, 0x1c7: 0x3317, 0x1c8: 0x3097, 0x1c9: 0x33a3, 0x1ca: 0x309c, 0x1cb: 0x33a8, + 0x1cc: 0x3141, 0x1cd: 0x3452, 0x1ce: 0x3146, 0x1cf: 0x3457, 0x1d0: 0x3164, 0x1d1: 0x3475, + 0x1d2: 0x3169, 0x1d3: 0x347a, 0x1d4: 0x31d7, 0x1d5: 0x34ed, 0x1d6: 0x31dc, 0x1d7: 0x34f2, + 0x1d8: 0x3182, 0x1d9: 0x3493, 0x1da: 0x319b, 0x1db: 0x34b1, + 0x1de: 0x3056, 0x1df: 0x3362, + 0x1e6: 0x4724, 0x1e7: 0x47b5, 0x1e8: 0x474c, 0x1e9: 0x47dd, + 0x1ea: 0x395b, 0x1eb: 0x3aea, 0x1ec: 0x3938, 0x1ed: 0x3ac7, 0x1ee: 0x476a, 0x1ef: 0x47fb, + 0x1f0: 0x3954, 0x1f1: 0x3ae3, 0x1f2: 0x3240, 0x1f3: 0x355b, + // Block 0x8, offset 0x200 + 0x200: 0x9932, 0x201: 0x9932, 0x202: 0x9932, 0x203: 0x9932, 0x204: 0x9932, 0x205: 0x8132, + 0x206: 0x9932, 0x207: 0x9932, 0x208: 0x9932, 0x209: 0x9932, 0x20a: 0x9932, 0x20b: 0x9932, + 0x20c: 0x9932, 0x20d: 0x8132, 0x20e: 0x8132, 0x20f: 0x9932, 0x210: 0x8132, 0x211: 0x9932, + 0x212: 0x8132, 0x213: 0x9932, 0x214: 0x9932, 0x215: 0x8133, 0x216: 0x812d, 0x217: 0x812d, + 0x218: 0x812d, 0x219: 0x812d, 0x21a: 0x8133, 0x21b: 0x992b, 0x21c: 0x812d, 0x21d: 0x812d, + 0x21e: 0x812d, 0x21f: 0x812d, 0x220: 0x812d, 0x221: 0x8129, 0x222: 0x8129, 0x223: 0x992d, + 0x224: 0x992d, 0x225: 0x992d, 0x226: 0x992d, 0x227: 0x9929, 0x228: 0x9929, 0x229: 0x812d, + 0x22a: 0x812d, 0x22b: 0x812d, 0x22c: 0x812d, 0x22d: 0x992d, 0x22e: 0x992d, 0x22f: 0x812d, + 0x230: 0x992d, 0x231: 0x992d, 0x232: 0x812d, 0x233: 0x812d, 0x234: 0x8101, 0x235: 0x8101, + 0x236: 0x8101, 0x237: 0x8101, 0x238: 0x9901, 0x239: 0x812d, 0x23a: 0x812d, 0x23b: 0x812d, + 0x23c: 0x812d, 0x23d: 0x8132, 0x23e: 0x8132, 0x23f: 0x8132, + // Block 0x9, offset 0x240 + 0x240: 0x4a40, 0x241: 0x4a45, 0x242: 0x9932, 0x243: 0x4a4a, 0x244: 0x4a4f, 0x245: 0x9936, + 0x246: 0x8132, 0x247: 0x812d, 0x248: 0x812d, 0x249: 0x812d, 0x24a: 0x8132, 0x24b: 0x8132, + 0x24c: 0x8132, 0x24d: 0x812d, 0x24e: 0x812d, 0x250: 0x8132, 0x251: 0x8132, + 0x252: 0x8132, 0x253: 0x812d, 0x254: 0x812d, 0x255: 0x812d, 0x256: 0x812d, 0x257: 0x8132, + 0x258: 0x8133, 0x259: 0x812d, 0x25a: 0x812d, 0x25b: 0x8132, 0x25c: 0x8134, 0x25d: 0x8135, + 0x25e: 0x8135, 0x25f: 0x8134, 0x260: 0x8135, 0x261: 0x8135, 0x262: 0x8134, 0x263: 0x8132, + 0x264: 0x8132, 0x265: 0x8132, 0x266: 0x8132, 0x267: 0x8132, 0x268: 0x8132, 0x269: 0x8132, + 0x26a: 0x8132, 0x26b: 0x8132, 0x26c: 0x8132, 0x26d: 0x8132, 0x26e: 0x8132, 0x26f: 0x8132, + 0x274: 0x0170, + 0x27a: 0x42a1, + 0x27e: 0x0037, + // Block 0xa, offset 0x280 + 0x284: 0x4256, 0x285: 0x450d, + 0x286: 0x35e5, 0x287: 0x00ce, 0x288: 0x3603, 0x289: 0x360f, 0x28a: 0x3621, + 0x28c: 0x363f, 0x28e: 0x3651, 0x28f: 0x366f, 0x290: 0x3e04, 0x291: 0xa000, + 0x295: 0xa000, 0x297: 0xa000, + 0x299: 0xa000, + 0x29f: 0xa000, 0x2a1: 0xa000, + 0x2a5: 0xa000, 0x2a9: 0xa000, + 0x2aa: 0x3633, 0x2ab: 0x3663, 0x2ac: 0x4890, 0x2ad: 0x3693, 0x2ae: 0x48ba, 0x2af: 0x36a5, + 0x2b0: 0x3e6c, 0x2b1: 0xa000, 0x2b5: 0xa000, + 0x2b7: 0xa000, 0x2b9: 0xa000, + 0x2bf: 0xa000, + // Block 0xb, offset 0x2c0 + 0x2c1: 0xa000, 0x2c5: 0xa000, + 0x2c9: 0xa000, 0x2ca: 0x48d2, 0x2cb: 0x48f0, + 0x2cc: 0x36c3, 0x2cd: 0x36db, 0x2ce: 0x4908, 0x2d0: 0x01be, 0x2d1: 0x01d0, + 0x2d2: 0x01ac, 0x2d3: 0x439e, 0x2d4: 0x43a4, 0x2d5: 0x01fa, 0x2d6: 0x01e8, + 0x2f0: 0x01d6, 0x2f1: 0x01eb, 0x2f2: 0x01ee, 0x2f4: 0x0188, 0x2f5: 0x01c7, + 0x2f9: 0x01a6, + // Block 0xc, offset 0x300 + 0x300: 0x371d, 0x301: 0x3729, 0x303: 0x3717, + 0x306: 0xa000, 0x307: 0x3705, + 0x30c: 0x3759, 0x30d: 0x3741, 0x30e: 0x376b, 0x310: 0xa000, + 0x313: 0xa000, 0x315: 0xa000, 0x316: 0xa000, 0x317: 0xa000, + 0x318: 0xa000, 0x319: 0x374d, 0x31a: 0xa000, + 0x31e: 0xa000, 0x323: 0xa000, + 0x327: 0xa000, + 0x32b: 0xa000, 0x32d: 0xa000, + 0x330: 0xa000, 0x333: 0xa000, 0x335: 0xa000, + 0x336: 0xa000, 0x337: 0xa000, 0x338: 0xa000, 0x339: 0x37d1, 0x33a: 0xa000, + 0x33e: 0xa000, + // Block 0xd, offset 0x340 + 0x341: 0x372f, 0x342: 0x37b3, + 0x350: 0x370b, 0x351: 0x378f, + 0x352: 0x3711, 0x353: 0x3795, 0x356: 0x3723, 0x357: 0x37a7, + 0x358: 0xa000, 0x359: 0xa000, 0x35a: 0x3825, 0x35b: 0x382b, 0x35c: 0x3735, 0x35d: 0x37b9, + 0x35e: 0x373b, 0x35f: 0x37bf, 0x362: 0x3747, 0x363: 0x37cb, + 0x364: 0x3753, 0x365: 0x37d7, 0x366: 0x375f, 0x367: 0x37e3, 0x368: 0xa000, 0x369: 0xa000, + 0x36a: 0x3831, 0x36b: 0x3837, 0x36c: 0x3789, 0x36d: 0x380d, 0x36e: 0x3765, 0x36f: 0x37e9, + 0x370: 0x3771, 0x371: 0x37f5, 0x372: 0x3777, 0x373: 0x37fb, 0x374: 0x377d, 0x375: 0x3801, + 0x378: 0x3783, 0x379: 0x3807, + // Block 0xe, offset 0x380 + 0x387: 0x1d5d, + 0x391: 0x812d, + 0x392: 0x8132, 0x393: 0x8132, 0x394: 0x8132, 0x395: 0x8132, 0x396: 0x812d, 0x397: 0x8132, + 0x398: 0x8132, 0x399: 0x8132, 0x39a: 0x812e, 0x39b: 0x812d, 0x39c: 0x8132, 0x39d: 0x8132, + 0x39e: 0x8132, 0x39f: 0x8132, 0x3a0: 0x8132, 0x3a1: 0x8132, 0x3a2: 0x812d, 0x3a3: 0x812d, + 0x3a4: 0x812d, 0x3a5: 0x812d, 0x3a6: 0x812d, 0x3a7: 0x812d, 0x3a8: 0x8132, 0x3a9: 0x8132, + 0x3aa: 0x812d, 0x3ab: 0x8132, 0x3ac: 0x8132, 0x3ad: 0x812e, 0x3ae: 0x8131, 0x3af: 0x8132, + 0x3b0: 0x8105, 0x3b1: 0x8106, 0x3b2: 0x8107, 0x3b3: 0x8108, 0x3b4: 0x8109, 0x3b5: 0x810a, + 0x3b6: 0x810b, 0x3b7: 0x810c, 0x3b8: 0x810d, 0x3b9: 0x810e, 0x3ba: 0x810e, 0x3bb: 0x810f, + 0x3bc: 0x8110, 0x3bd: 0x8111, 0x3bf: 0x8112, + // Block 0xf, offset 0x3c0 + 0x3c8: 0xa000, 0x3ca: 0xa000, 0x3cb: 0x8116, + 0x3cc: 0x8117, 0x3cd: 0x8118, 0x3ce: 0x8119, 0x3cf: 0x811a, 0x3d0: 0x811b, 0x3d1: 0x811c, + 0x3d2: 0x811d, 0x3d3: 0x9932, 0x3d4: 0x9932, 0x3d5: 0x992d, 0x3d6: 0x812d, 0x3d7: 0x8132, + 0x3d8: 0x8132, 0x3d9: 0x8132, 0x3da: 0x8132, 0x3db: 0x8132, 0x3dc: 0x812d, 0x3dd: 0x8132, + 0x3de: 0x8132, 0x3df: 0x812d, + 0x3f0: 0x811e, 0x3f5: 0x1d80, + 0x3f6: 0x200f, 0x3f7: 0x204b, 0x3f8: 0x2046, + // Block 0x10, offset 0x400 + 0x405: 0xa000, + 0x406: 0x2d22, 0x407: 0xa000, 0x408: 0x2d2a, 0x409: 0xa000, 0x40a: 0x2d32, 0x40b: 0xa000, + 0x40c: 0x2d3a, 0x40d: 0xa000, 0x40e: 0x2d42, 0x411: 0xa000, + 0x412: 0x2d4a, + 0x434: 0x8102, 0x435: 0x9900, + 0x43a: 0xa000, 0x43b: 0x2d52, + 0x43c: 0xa000, 0x43d: 0x2d5a, 0x43e: 0xa000, 0x43f: 0xa000, + // Block 0x11, offset 0x440 + 0x440: 0x0069, 0x441: 0x006b, 0x442: 0x006f, 0x443: 0x0083, 0x444: 0x00f5, 0x445: 0x00f8, + 0x446: 0x0413, 0x447: 0x0085, 0x448: 0x0089, 0x449: 0x008b, 0x44a: 0x0104, 0x44b: 0x0107, + 0x44c: 0x010a, 0x44d: 0x008f, 0x44f: 0x0097, 0x450: 0x009b, 0x451: 0x00e0, + 0x452: 0x009f, 0x453: 0x00fe, 0x454: 0x0417, 0x455: 0x041b, 0x456: 0x00a1, 0x457: 0x00a9, + 0x458: 0x00ab, 0x459: 0x0423, 0x45a: 0x012b, 0x45b: 0x00ad, 0x45c: 0x0427, 0x45d: 0x01be, + 0x45e: 0x01c1, 0x45f: 0x01c4, 0x460: 0x01fa, 0x461: 0x01fd, 0x462: 0x0093, 0x463: 0x00a5, + 0x464: 0x00ab, 0x465: 0x00ad, 0x466: 0x01be, 0x467: 0x01c1, 0x468: 0x01eb, 0x469: 0x01fa, + 0x46a: 0x01fd, + 0x478: 0x020c, + // Block 0x12, offset 0x480 + 0x49b: 0x00fb, 0x49c: 0x0087, 0x49d: 0x0101, + 0x49e: 0x00d4, 0x49f: 0x010a, 0x4a0: 0x008d, 0x4a1: 0x010d, 0x4a2: 0x0110, 0x4a3: 0x0116, + 0x4a4: 0x011c, 0x4a5: 0x011f, 0x4a6: 0x0122, 0x4a7: 0x042b, 0x4a8: 0x016a, 0x4a9: 0x0128, + 0x4aa: 0x042f, 0x4ab: 0x016d, 0x4ac: 0x0131, 0x4ad: 0x012e, 0x4ae: 0x0134, 0x4af: 0x0137, + 0x4b0: 0x013a, 0x4b1: 0x013d, 0x4b2: 0x0140, 0x4b3: 0x014c, 0x4b4: 0x014f, 0x4b5: 0x00ec, + 0x4b6: 0x0152, 0x4b7: 0x0155, 0x4b8: 0x041f, 0x4b9: 0x0158, 0x4ba: 0x015b, 0x4bb: 0x00b5, + 0x4bc: 0x015e, 0x4bd: 0x0161, 0x4be: 0x0164, 0x4bf: 0x01d0, + // Block 0x13, offset 0x4c0 + 0x4c0: 0x2f93, 0x4c1: 0x329f, 0x4c2: 0x2f9d, 0x4c3: 0x32a9, 0x4c4: 0x2fa2, 0x4c5: 0x32ae, + 0x4c6: 0x2fa7, 0x4c7: 0x32b3, 0x4c8: 0x38c8, 0x4c9: 0x3a57, 0x4ca: 0x2fc0, 0x4cb: 0x32cc, + 0x4cc: 0x2fca, 0x4cd: 0x32d6, 0x4ce: 0x2fd9, 0x4cf: 0x32e5, 0x4d0: 0x2fcf, 0x4d1: 0x32db, + 0x4d2: 0x2fd4, 0x4d3: 0x32e0, 0x4d4: 0x38eb, 0x4d5: 0x3a7a, 0x4d6: 0x38f2, 0x4d7: 0x3a81, + 0x4d8: 0x3015, 0x4d9: 0x3321, 0x4da: 0x301a, 0x4db: 0x3326, 0x4dc: 0x3900, 0x4dd: 0x3a8f, + 0x4de: 0x301f, 0x4df: 0x332b, 0x4e0: 0x302e, 0x4e1: 0x333a, 0x4e2: 0x304c, 0x4e3: 0x3358, + 0x4e4: 0x305b, 0x4e5: 0x3367, 0x4e6: 0x3051, 0x4e7: 0x335d, 0x4e8: 0x3060, 0x4e9: 0x336c, + 0x4ea: 0x3065, 0x4eb: 0x3371, 0x4ec: 0x30ab, 0x4ed: 0x33b7, 0x4ee: 0x3907, 0x4ef: 0x3a96, + 0x4f0: 0x30b5, 0x4f1: 0x33c6, 0x4f2: 0x30bf, 0x4f3: 0x33d0, 0x4f4: 0x30c9, 0x4f5: 0x33da, + 0x4f6: 0x4756, 0x4f7: 0x47e7, 0x4f8: 0x390e, 0x4f9: 0x3a9d, 0x4fa: 0x30e2, 0x4fb: 0x33f3, + 0x4fc: 0x30dd, 0x4fd: 0x33ee, 0x4fe: 0x30e7, 0x4ff: 0x33f8, + // Block 0x14, offset 0x500 + 0x500: 0x30ec, 0x501: 0x33fd, 0x502: 0x30f1, 0x503: 0x3402, 0x504: 0x3105, 0x505: 0x3416, + 0x506: 0x310f, 0x507: 0x3420, 0x508: 0x311e, 0x509: 0x342f, 0x50a: 0x3119, 0x50b: 0x342a, + 0x50c: 0x3931, 0x50d: 0x3ac0, 0x50e: 0x393f, 0x50f: 0x3ace, 0x510: 0x3946, 0x511: 0x3ad5, + 0x512: 0x394d, 0x513: 0x3adc, 0x514: 0x314b, 0x515: 0x345c, 0x516: 0x3150, 0x517: 0x3461, + 0x518: 0x315a, 0x519: 0x346b, 0x51a: 0x4783, 0x51b: 0x4814, 0x51c: 0x3993, 0x51d: 0x3b22, + 0x51e: 0x3173, 0x51f: 0x3484, 0x520: 0x317d, 0x521: 0x348e, 0x522: 0x4792, 0x523: 0x4823, + 0x524: 0x399a, 0x525: 0x3b29, 0x526: 0x39a1, 0x527: 0x3b30, 0x528: 0x39a8, 0x529: 0x3b37, + 0x52a: 0x318c, 0x52b: 0x349d, 0x52c: 0x3196, 0x52d: 0x34ac, 0x52e: 0x31aa, 0x52f: 0x34c0, + 0x530: 0x31a5, 0x531: 0x34bb, 0x532: 0x31e6, 0x533: 0x34fc, 0x534: 0x31f5, 0x535: 0x350b, + 0x536: 0x31f0, 0x537: 0x3506, 0x538: 0x39af, 0x539: 0x3b3e, 0x53a: 0x39b6, 0x53b: 0x3b45, + 0x53c: 0x31fa, 0x53d: 0x3510, 0x53e: 0x31ff, 0x53f: 0x3515, + // Block 0x15, offset 0x540 + 0x540: 0x3204, 0x541: 0x351a, 0x542: 0x3209, 0x543: 0x351f, 0x544: 0x3218, 0x545: 0x352e, + 0x546: 0x3213, 0x547: 0x3529, 0x548: 0x321d, 0x549: 0x3538, 0x54a: 0x3222, 0x54b: 0x353d, + 0x54c: 0x3227, 0x54d: 0x3542, 0x54e: 0x3245, 0x54f: 0x3560, 0x550: 0x325e, 0x551: 0x357e, + 0x552: 0x326d, 0x553: 0x358d, 0x554: 0x3272, 0x555: 0x3592, 0x556: 0x3376, 0x557: 0x34a2, + 0x558: 0x3533, 0x559: 0x356f, 0x55a: 0x1bdc, 0x55b: 0x42d3, + 0x560: 0x4733, 0x561: 0x47c4, 0x562: 0x2f7f, 0x563: 0x328b, + 0x564: 0x3874, 0x565: 0x3a03, 0x566: 0x386d, 0x567: 0x39fc, 0x568: 0x3882, 0x569: 0x3a11, + 0x56a: 0x387b, 0x56b: 0x3a0a, 0x56c: 0x38ba, 0x56d: 0x3a49, 0x56e: 0x3890, 0x56f: 0x3a1f, + 0x570: 0x3889, 0x571: 0x3a18, 0x572: 0x389e, 0x573: 0x3a2d, 0x574: 0x3897, 0x575: 0x3a26, + 0x576: 0x38c1, 0x577: 0x3a50, 0x578: 0x4747, 0x579: 0x47d8, 0x57a: 0x2ffc, 0x57b: 0x3308, + 0x57c: 0x2fe8, 0x57d: 0x32f4, 0x57e: 0x38d6, 0x57f: 0x3a65, + // Block 0x16, offset 0x580 + 0x580: 0x38cf, 0x581: 0x3a5e, 0x582: 0x38e4, 0x583: 0x3a73, 0x584: 0x38dd, 0x585: 0x3a6c, + 0x586: 0x38f9, 0x587: 0x3a88, 0x588: 0x308d, 0x589: 0x3399, 0x58a: 0x30a1, 0x58b: 0x33ad, + 0x58c: 0x4779, 0x58d: 0x480a, 0x58e: 0x3132, 0x58f: 0x3443, 0x590: 0x391c, 0x591: 0x3aab, + 0x592: 0x3915, 0x593: 0x3aa4, 0x594: 0x392a, 0x595: 0x3ab9, 0x596: 0x3923, 0x597: 0x3ab2, + 0x598: 0x3985, 0x599: 0x3b14, 0x59a: 0x3969, 0x59b: 0x3af8, 0x59c: 0x3962, 0x59d: 0x3af1, + 0x59e: 0x3977, 0x59f: 0x3b06, 0x5a0: 0x3970, 0x5a1: 0x3aff, 0x5a2: 0x397e, 0x5a3: 0x3b0d, + 0x5a4: 0x31e1, 0x5a5: 0x34f7, 0x5a6: 0x31c3, 0x5a7: 0x34d9, 0x5a8: 0x39e0, 0x5a9: 0x3b6f, + 0x5aa: 0x39d9, 0x5ab: 0x3b68, 0x5ac: 0x39ee, 0x5ad: 0x3b7d, 0x5ae: 0x39e7, 0x5af: 0x3b76, + 0x5b0: 0x39f5, 0x5b1: 0x3b84, 0x5b2: 0x322c, 0x5b3: 0x3547, 0x5b4: 0x3254, 0x5b5: 0x3574, + 0x5b6: 0x324f, 0x5b7: 0x356a, 0x5b8: 0x323b, 0x5b9: 0x3556, + // Block 0x17, offset 0x5c0 + 0x5c0: 0x4896, 0x5c1: 0x489c, 0x5c2: 0x49b0, 0x5c3: 0x49c8, 0x5c4: 0x49b8, 0x5c5: 0x49d0, + 0x5c6: 0x49c0, 0x5c7: 0x49d8, 0x5c8: 0x483c, 0x5c9: 0x4842, 0x5ca: 0x4920, 0x5cb: 0x4938, + 0x5cc: 0x4928, 0x5cd: 0x4940, 0x5ce: 0x4930, 0x5cf: 0x4948, 0x5d0: 0x48a8, 0x5d1: 0x48ae, + 0x5d2: 0x3db4, 0x5d3: 0x3dc4, 0x5d4: 0x3dbc, 0x5d5: 0x3dcc, + 0x5d8: 0x4848, 0x5d9: 0x484e, 0x5da: 0x3ce4, 0x5db: 0x3cf4, 0x5dc: 0x3cec, 0x5dd: 0x3cfc, + 0x5e0: 0x48c0, 0x5e1: 0x48c6, 0x5e2: 0x49e0, 0x5e3: 0x49f8, + 0x5e4: 0x49e8, 0x5e5: 0x4a00, 0x5e6: 0x49f0, 0x5e7: 0x4a08, 0x5e8: 0x4854, 0x5e9: 0x485a, + 0x5ea: 0x4950, 0x5eb: 0x4968, 0x5ec: 0x4958, 0x5ed: 0x4970, 0x5ee: 0x4960, 0x5ef: 0x4978, + 0x5f0: 0x48d8, 0x5f1: 0x48de, 0x5f2: 0x3e14, 0x5f3: 0x3e2c, 0x5f4: 0x3e1c, 0x5f5: 0x3e34, + 0x5f6: 0x3e24, 0x5f7: 0x3e3c, 0x5f8: 0x4860, 0x5f9: 0x4866, 0x5fa: 0x3d14, 0x5fb: 0x3d2c, + 0x5fc: 0x3d1c, 0x5fd: 0x3d34, 0x5fe: 0x3d24, 0x5ff: 0x3d3c, + // Block 0x18, offset 0x600 + 0x600: 0x48e4, 0x601: 0x48ea, 0x602: 0x3e44, 0x603: 0x3e54, 0x604: 0x3e4c, 0x605: 0x3e5c, + 0x608: 0x486c, 0x609: 0x4872, 0x60a: 0x3d44, 0x60b: 0x3d54, + 0x60c: 0x3d4c, 0x60d: 0x3d5c, 0x610: 0x48f6, 0x611: 0x48fc, + 0x612: 0x3e7c, 0x613: 0x3e94, 0x614: 0x3e84, 0x615: 0x3e9c, 0x616: 0x3e8c, 0x617: 0x3ea4, + 0x619: 0x4878, 0x61b: 0x3d64, 0x61d: 0x3d6c, + 0x61f: 0x3d74, 0x620: 0x490e, 0x621: 0x4914, 0x622: 0x4a10, 0x623: 0x4a28, + 0x624: 0x4a18, 0x625: 0x4a30, 0x626: 0x4a20, 0x627: 0x4a38, 0x628: 0x487e, 0x629: 0x4884, + 0x62a: 0x4980, 0x62b: 0x4998, 0x62c: 0x4988, 0x62d: 0x49a0, 0x62e: 0x4990, 0x62f: 0x49a8, + 0x630: 0x488a, 0x631: 0x43b0, 0x632: 0x368d, 0x633: 0x43b6, 0x634: 0x48b4, 0x635: 0x43bc, + 0x636: 0x369f, 0x637: 0x43c2, 0x638: 0x36bd, 0x639: 0x43c8, 0x63a: 0x36d5, 0x63b: 0x43ce, + 0x63c: 0x4902, 0x63d: 0x43d4, + // Block 0x19, offset 0x640 + 0x640: 0x3d9c, 0x641: 0x3da4, 0x642: 0x4180, 0x643: 0x419e, 0x644: 0x418a, 0x645: 0x41a8, + 0x646: 0x4194, 0x647: 0x41b2, 0x648: 0x3cd4, 0x649: 0x3cdc, 0x64a: 0x40cc, 0x64b: 0x40ea, + 0x64c: 0x40d6, 0x64d: 0x40f4, 0x64e: 0x40e0, 0x64f: 0x40fe, 0x650: 0x3de4, 0x651: 0x3dec, + 0x652: 0x41bc, 0x653: 0x41da, 0x654: 0x41c6, 0x655: 0x41e4, 0x656: 0x41d0, 0x657: 0x41ee, + 0x658: 0x3d04, 0x659: 0x3d0c, 0x65a: 0x4108, 0x65b: 0x4126, 0x65c: 0x4112, 0x65d: 0x4130, + 0x65e: 0x411c, 0x65f: 0x413a, 0x660: 0x3ebc, 0x661: 0x3ec4, 0x662: 0x41f8, 0x663: 0x4216, + 0x664: 0x4202, 0x665: 0x4220, 0x666: 0x420c, 0x667: 0x422a, 0x668: 0x3d7c, 0x669: 0x3d84, + 0x66a: 0x4144, 0x66b: 0x4162, 0x66c: 0x414e, 0x66d: 0x416c, 0x66e: 0x4158, 0x66f: 0x4176, + 0x670: 0x3681, 0x671: 0x367b, 0x672: 0x3d8c, 0x673: 0x3687, 0x674: 0x3d94, + 0x676: 0x48a2, 0x677: 0x3dac, 0x678: 0x35f1, 0x679: 0x35eb, 0x67a: 0x35df, 0x67b: 0x4380, + 0x67c: 0x35f7, 0x67d: 0x4283, 0x67e: 0x01d3, 0x67f: 0x4283, + // Block 0x1a, offset 0x680 + 0x680: 0x429c, 0x681: 0x4514, 0x682: 0x3dd4, 0x683: 0x3699, 0x684: 0x3ddc, + 0x686: 0x48cc, 0x687: 0x3df4, 0x688: 0x35fd, 0x689: 0x4386, 0x68a: 0x3609, 0x68b: 0x438c, + 0x68c: 0x3615, 0x68d: 0x451b, 0x68e: 0x4522, 0x68f: 0x4529, 0x690: 0x36b1, 0x691: 0x36ab, + 0x692: 0x3dfc, 0x693: 0x4576, 0x696: 0x36b7, 0x697: 0x3e0c, + 0x698: 0x362d, 0x699: 0x3627, 0x69a: 0x361b, 0x69b: 0x4392, 0x69d: 0x4530, + 0x69e: 0x4537, 0x69f: 0x453e, 0x6a0: 0x36e7, 0x6a1: 0x36e1, 0x6a2: 0x3e64, 0x6a3: 0x457e, + 0x6a4: 0x36c9, 0x6a5: 0x36cf, 0x6a6: 0x36ed, 0x6a7: 0x3e74, 0x6a8: 0x365d, 0x6a9: 0x3657, + 0x6aa: 0x364b, 0x6ab: 0x439e, 0x6ac: 0x3645, 0x6ad: 0x4506, 0x6ae: 0x450d, 0x6af: 0x0081, + 0x6b2: 0x3eac, 0x6b3: 0x36f3, 0x6b4: 0x3eb4, + 0x6b6: 0x491a, 0x6b7: 0x3ecc, 0x6b8: 0x3639, 0x6b9: 0x4398, 0x6ba: 0x3669, 0x6bb: 0x43aa, + 0x6bc: 0x3675, 0x6bd: 0x4256, 0x6be: 0x4288, + // Block 0x1b, offset 0x6c0 + 0x6c0: 0x1bd4, 0x6c1: 0x1bd8, 0x6c2: 0x0047, 0x6c3: 0x1c50, 0x6c5: 0x1be4, + 0x6c6: 0x1be8, 0x6c7: 0x00e9, 0x6c9: 0x1c54, 0x6ca: 0x008f, 0x6cb: 0x0051, + 0x6cc: 0x0051, 0x6cd: 0x0051, 0x6ce: 0x0091, 0x6cf: 0x00da, 0x6d0: 0x0053, 0x6d1: 0x0053, + 0x6d2: 0x0059, 0x6d3: 0x0099, 0x6d5: 0x005d, 0x6d6: 0x1989, + 0x6d9: 0x0061, 0x6da: 0x0063, 0x6db: 0x0065, 0x6dc: 0x0065, 0x6dd: 0x0065, + 0x6e0: 0x199b, 0x6e1: 0x1bc4, 0x6e2: 0x19a4, + 0x6e4: 0x0075, 0x6e6: 0x01b8, 0x6e8: 0x0075, + 0x6ea: 0x0057, 0x6eb: 0x42ce, 0x6ec: 0x0045, 0x6ed: 0x0047, 0x6ef: 0x008b, + 0x6f0: 0x004b, 0x6f1: 0x004d, 0x6f3: 0x005b, 0x6f4: 0x009f, 0x6f5: 0x0215, + 0x6f6: 0x0218, 0x6f7: 0x021b, 0x6f8: 0x021e, 0x6f9: 0x0093, 0x6fb: 0x1b94, + 0x6fc: 0x01e8, 0x6fd: 0x01c1, 0x6fe: 0x0179, 0x6ff: 0x01a0, + // Block 0x1c, offset 0x700 + 0x700: 0x0463, 0x705: 0x0049, + 0x706: 0x0089, 0x707: 0x008b, 0x708: 0x0093, 0x709: 0x0095, + 0x710: 0x222a, 0x711: 0x2236, + 0x712: 0x22ea, 0x713: 0x2212, 0x714: 0x2296, 0x715: 0x221e, 0x716: 0x229c, 0x717: 0x22b4, + 0x718: 0x22c0, 0x719: 0x2224, 0x71a: 0x22c6, 0x71b: 0x2230, 0x71c: 0x22ba, 0x71d: 0x22cc, + 0x71e: 0x22d2, 0x71f: 0x1cb8, 0x720: 0x0053, 0x721: 0x1956, 0x722: 0x1ba0, 0x723: 0x195f, + 0x724: 0x006d, 0x725: 0x19a7, 0x726: 0x1bcc, 0x727: 0x1d44, 0x728: 0x1962, 0x729: 0x0071, + 0x72a: 0x19b3, 0x72b: 0x1bd0, 0x72c: 0x0059, 0x72d: 0x0047, 0x72e: 0x0049, 0x72f: 0x005b, + 0x730: 0x0093, 0x731: 0x19e0, 0x732: 0x1c14, 0x733: 0x19e9, 0x734: 0x00ad, 0x735: 0x1a5e, + 0x736: 0x1c48, 0x737: 0x1d58, 0x738: 0x19ec, 0x739: 0x00b1, 0x73a: 0x1a61, 0x73b: 0x1c4c, + 0x73c: 0x0099, 0x73d: 0x0087, 0x73e: 0x0089, 0x73f: 0x009b, + // Block 0x1d, offset 0x740 + 0x741: 0x3c02, 0x743: 0xa000, 0x744: 0x3c09, 0x745: 0xa000, + 0x747: 0x3c10, 0x748: 0xa000, 0x749: 0x3c17, + 0x74d: 0xa000, + 0x760: 0x2f61, 0x761: 0xa000, 0x762: 0x3c25, + 0x764: 0xa000, 0x765: 0xa000, + 0x76d: 0x3c1e, 0x76e: 0x2f5c, 0x76f: 0x2f66, + 0x770: 0x3c2c, 0x771: 0x3c33, 0x772: 0xa000, 0x773: 0xa000, 0x774: 0x3c3a, 0x775: 0x3c41, + 0x776: 0xa000, 0x777: 0xa000, 0x778: 0x3c48, 0x779: 0x3c4f, 0x77a: 0xa000, 0x77b: 0xa000, + 0x77c: 0xa000, 0x77d: 0xa000, + // Block 0x1e, offset 0x780 + 0x780: 0x3c56, 0x781: 0x3c5d, 0x782: 0xa000, 0x783: 0xa000, 0x784: 0x3c72, 0x785: 0x3c79, + 0x786: 0xa000, 0x787: 0xa000, 0x788: 0x3c80, 0x789: 0x3c87, + 0x791: 0xa000, + 0x792: 0xa000, + 0x7a2: 0xa000, + 0x7a8: 0xa000, 0x7a9: 0xa000, + 0x7ab: 0xa000, 0x7ac: 0x3c9c, 0x7ad: 0x3ca3, 0x7ae: 0x3caa, 0x7af: 0x3cb1, + 0x7b2: 0xa000, 0x7b3: 0xa000, 0x7b4: 0xa000, 0x7b5: 0xa000, + // Block 0x1f, offset 0x7c0 + 0x7e0: 0x0023, 0x7e1: 0x0025, 0x7e2: 0x0027, 0x7e3: 0x0029, + 0x7e4: 0x002b, 0x7e5: 0x002d, 0x7e6: 0x002f, 0x7e7: 0x0031, 0x7e8: 0x0033, 0x7e9: 0x187e, + 0x7ea: 0x1881, 0x7eb: 0x1884, 0x7ec: 0x1887, 0x7ed: 0x188a, 0x7ee: 0x188d, 0x7ef: 0x1890, + 0x7f0: 0x1893, 0x7f1: 0x1896, 0x7f2: 0x1899, 0x7f3: 0x18a2, 0x7f4: 0x1a64, 0x7f5: 0x1a68, + 0x7f6: 0x1a6c, 0x7f7: 0x1a70, 0x7f8: 0x1a74, 0x7f9: 0x1a78, 0x7fa: 0x1a7c, 0x7fb: 0x1a80, + 0x7fc: 0x1a84, 0x7fd: 0x1c7c, 0x7fe: 0x1c81, 0x7ff: 0x1c86, + // Block 0x20, offset 0x800 + 0x800: 0x1c8b, 0x801: 0x1c90, 0x802: 0x1c95, 0x803: 0x1c9a, 0x804: 0x1c9f, 0x805: 0x1ca4, + 0x806: 0x1ca9, 0x807: 0x1cae, 0x808: 0x187b, 0x809: 0x189f, 0x80a: 0x18c3, 0x80b: 0x18e7, + 0x80c: 0x190b, 0x80d: 0x1914, 0x80e: 0x191a, 0x80f: 0x1920, 0x810: 0x1926, 0x811: 0x1b5c, + 0x812: 0x1b60, 0x813: 0x1b64, 0x814: 0x1b68, 0x815: 0x1b6c, 0x816: 0x1b70, 0x817: 0x1b74, + 0x818: 0x1b78, 0x819: 0x1b7c, 0x81a: 0x1b80, 0x81b: 0x1b84, 0x81c: 0x1af0, 0x81d: 0x1af4, + 0x81e: 0x1af8, 0x81f: 0x1afc, 0x820: 0x1b00, 0x821: 0x1b04, 0x822: 0x1b08, 0x823: 0x1b0c, + 0x824: 0x1b10, 0x825: 0x1b14, 0x826: 0x1b18, 0x827: 0x1b1c, 0x828: 0x1b20, 0x829: 0x1b24, + 0x82a: 0x1b28, 0x82b: 0x1b2c, 0x82c: 0x1b30, 0x82d: 0x1b34, 0x82e: 0x1b38, 0x82f: 0x1b3c, + 0x830: 0x1b40, 0x831: 0x1b44, 0x832: 0x1b48, 0x833: 0x1b4c, 0x834: 0x1b50, 0x835: 0x1b54, + 0x836: 0x0043, 0x837: 0x0045, 0x838: 0x0047, 0x839: 0x0049, 0x83a: 0x004b, 0x83b: 0x004d, + 0x83c: 0x004f, 0x83d: 0x0051, 0x83e: 0x0053, 0x83f: 0x0055, + // Block 0x21, offset 0x840 + 0x840: 0x06bf, 0x841: 0x06e3, 0x842: 0x06ef, 0x843: 0x06ff, 0x844: 0x0707, 0x845: 0x0713, + 0x846: 0x071b, 0x847: 0x0723, 0x848: 0x072f, 0x849: 0x0783, 0x84a: 0x079b, 0x84b: 0x07ab, + 0x84c: 0x07bb, 0x84d: 0x07cb, 0x84e: 0x07db, 0x84f: 0x07fb, 0x850: 0x07ff, 0x851: 0x0803, + 0x852: 0x0837, 0x853: 0x085f, 0x854: 0x086f, 0x855: 0x0877, 0x856: 0x087b, 0x857: 0x0887, + 0x858: 0x08a3, 0x859: 0x08a7, 0x85a: 0x08bf, 0x85b: 0x08c3, 0x85c: 0x08cb, 0x85d: 0x08db, + 0x85e: 0x0977, 0x85f: 0x098b, 0x860: 0x09cb, 0x861: 0x09df, 0x862: 0x09e7, 0x863: 0x09eb, + 0x864: 0x09fb, 0x865: 0x0a17, 0x866: 0x0a43, 0x867: 0x0a4f, 0x868: 0x0a6f, 0x869: 0x0a7b, + 0x86a: 0x0a7f, 0x86b: 0x0a83, 0x86c: 0x0a9b, 0x86d: 0x0a9f, 0x86e: 0x0acb, 0x86f: 0x0ad7, + 0x870: 0x0adf, 0x871: 0x0ae7, 0x872: 0x0af7, 0x873: 0x0aff, 0x874: 0x0b07, 0x875: 0x0b33, + 0x876: 0x0b37, 0x877: 0x0b3f, 0x878: 0x0b43, 0x879: 0x0b4b, 0x87a: 0x0b53, 0x87b: 0x0b63, + 0x87c: 0x0b7f, 0x87d: 0x0bf7, 0x87e: 0x0c0b, 0x87f: 0x0c0f, + // Block 0x22, offset 0x880 + 0x880: 0x0c8f, 0x881: 0x0c93, 0x882: 0x0ca7, 0x883: 0x0cab, 0x884: 0x0cb3, 0x885: 0x0cbb, + 0x886: 0x0cc3, 0x887: 0x0ccf, 0x888: 0x0cf7, 0x889: 0x0d07, 0x88a: 0x0d1b, 0x88b: 0x0d8b, + 0x88c: 0x0d97, 0x88d: 0x0da7, 0x88e: 0x0db3, 0x88f: 0x0dbf, 0x890: 0x0dc7, 0x891: 0x0dcb, + 0x892: 0x0dcf, 0x893: 0x0dd3, 0x894: 0x0dd7, 0x895: 0x0e8f, 0x896: 0x0ed7, 0x897: 0x0ee3, + 0x898: 0x0ee7, 0x899: 0x0eeb, 0x89a: 0x0eef, 0x89b: 0x0ef7, 0x89c: 0x0efb, 0x89d: 0x0f0f, + 0x89e: 0x0f2b, 0x89f: 0x0f33, 0x8a0: 0x0f73, 0x8a1: 0x0f77, 0x8a2: 0x0f7f, 0x8a3: 0x0f83, + 0x8a4: 0x0f8b, 0x8a5: 0x0f8f, 0x8a6: 0x0fb3, 0x8a7: 0x0fb7, 0x8a8: 0x0fd3, 0x8a9: 0x0fd7, + 0x8aa: 0x0fdb, 0x8ab: 0x0fdf, 0x8ac: 0x0ff3, 0x8ad: 0x1017, 0x8ae: 0x101b, 0x8af: 0x101f, + 0x8b0: 0x1043, 0x8b1: 0x1083, 0x8b2: 0x1087, 0x8b3: 0x10a7, 0x8b4: 0x10b7, 0x8b5: 0x10bf, + 0x8b6: 0x10df, 0x8b7: 0x1103, 0x8b8: 0x1147, 0x8b9: 0x114f, 0x8ba: 0x1163, 0x8bb: 0x116f, + 0x8bc: 0x1177, 0x8bd: 0x117f, 0x8be: 0x1183, 0x8bf: 0x1187, + // Block 0x23, offset 0x8c0 + 0x8c0: 0x119f, 0x8c1: 0x11a3, 0x8c2: 0x11bf, 0x8c3: 0x11c7, 0x8c4: 0x11cf, 0x8c5: 0x11d3, + 0x8c6: 0x11df, 0x8c7: 0x11e7, 0x8c8: 0x11eb, 0x8c9: 0x11ef, 0x8ca: 0x11f7, 0x8cb: 0x11fb, + 0x8cc: 0x129b, 0x8cd: 0x12af, 0x8ce: 0x12e3, 0x8cf: 0x12e7, 0x8d0: 0x12ef, 0x8d1: 0x131b, + 0x8d2: 0x1323, 0x8d3: 0x132b, 0x8d4: 0x1333, 0x8d5: 0x136f, 0x8d6: 0x1373, 0x8d7: 0x137b, + 0x8d8: 0x137f, 0x8d9: 0x1383, 0x8da: 0x13af, 0x8db: 0x13b3, 0x8dc: 0x13bb, 0x8dd: 0x13cf, + 0x8de: 0x13d3, 0x8df: 0x13ef, 0x8e0: 0x13f7, 0x8e1: 0x13fb, 0x8e2: 0x141f, 0x8e3: 0x143f, + 0x8e4: 0x144f, 0x8e5: 0x1453, 0x8e6: 0x145b, 0x8e7: 0x1487, 0x8e8: 0x148b, 0x8e9: 0x149b, + 0x8ea: 0x14bf, 0x8eb: 0x14cb, 0x8ec: 0x14db, 0x8ed: 0x14f3, 0x8ee: 0x14fb, 0x8ef: 0x14ff, + 0x8f0: 0x1503, 0x8f1: 0x1507, 0x8f2: 0x1513, 0x8f3: 0x1517, 0x8f4: 0x151f, 0x8f5: 0x153b, + 0x8f6: 0x153f, 0x8f7: 0x1543, 0x8f8: 0x155b, 0x8f9: 0x155f, 0x8fa: 0x1567, 0x8fb: 0x157b, + 0x8fc: 0x157f, 0x8fd: 0x1583, 0x8fe: 0x158b, 0x8ff: 0x158f, + // Block 0x24, offset 0x900 + 0x906: 0xa000, 0x90b: 0xa000, + 0x90c: 0x3f04, 0x90d: 0xa000, 0x90e: 0x3f0c, 0x90f: 0xa000, 0x910: 0x3f14, 0x911: 0xa000, + 0x912: 0x3f1c, 0x913: 0xa000, 0x914: 0x3f24, 0x915: 0xa000, 0x916: 0x3f2c, 0x917: 0xa000, + 0x918: 0x3f34, 0x919: 0xa000, 0x91a: 0x3f3c, 0x91b: 0xa000, 0x91c: 0x3f44, 0x91d: 0xa000, + 0x91e: 0x3f4c, 0x91f: 0xa000, 0x920: 0x3f54, 0x921: 0xa000, 0x922: 0x3f5c, + 0x924: 0xa000, 0x925: 0x3f64, 0x926: 0xa000, 0x927: 0x3f6c, 0x928: 0xa000, 0x929: 0x3f74, + 0x92f: 0xa000, + 0x930: 0x3f7c, 0x931: 0x3f84, 0x932: 0xa000, 0x933: 0x3f8c, 0x934: 0x3f94, 0x935: 0xa000, + 0x936: 0x3f9c, 0x937: 0x3fa4, 0x938: 0xa000, 0x939: 0x3fac, 0x93a: 0x3fb4, 0x93b: 0xa000, + 0x93c: 0x3fbc, 0x93d: 0x3fc4, + // Block 0x25, offset 0x940 + 0x954: 0x3efc, + 0x959: 0x9903, 0x95a: 0x9903, 0x95b: 0x436e, 0x95c: 0x4374, 0x95d: 0xa000, + 0x95e: 0x3fcc, 0x95f: 0x26b0, + 0x966: 0xa000, + 0x96b: 0xa000, 0x96c: 0x3fdc, 0x96d: 0xa000, 0x96e: 0x3fe4, 0x96f: 0xa000, + 0x970: 0x3fec, 0x971: 0xa000, 0x972: 0x3ff4, 0x973: 0xa000, 0x974: 0x3ffc, 0x975: 0xa000, + 0x976: 0x4004, 0x977: 0xa000, 0x978: 0x400c, 0x979: 0xa000, 0x97a: 0x4014, 0x97b: 0xa000, + 0x97c: 0x401c, 0x97d: 0xa000, 0x97e: 0x4024, 0x97f: 0xa000, + // Block 0x26, offset 0x980 + 0x980: 0x402c, 0x981: 0xa000, 0x982: 0x4034, 0x984: 0xa000, 0x985: 0x403c, + 0x986: 0xa000, 0x987: 0x4044, 0x988: 0xa000, 0x989: 0x404c, + 0x98f: 0xa000, 0x990: 0x4054, 0x991: 0x405c, + 0x992: 0xa000, 0x993: 0x4064, 0x994: 0x406c, 0x995: 0xa000, 0x996: 0x4074, 0x997: 0x407c, + 0x998: 0xa000, 0x999: 0x4084, 0x99a: 0x408c, 0x99b: 0xa000, 0x99c: 0x4094, 0x99d: 0x409c, + 0x9af: 0xa000, + 0x9b0: 0xa000, 0x9b1: 0xa000, 0x9b2: 0xa000, 0x9b4: 0x3fd4, + 0x9b7: 0x40a4, 0x9b8: 0x40ac, 0x9b9: 0x40b4, 0x9ba: 0x40bc, + 0x9bd: 0xa000, 0x9be: 0x40c4, 0x9bf: 0x26c5, + // Block 0x27, offset 0x9c0 + 0x9c0: 0x0367, 0x9c1: 0x032b, 0x9c2: 0x032f, 0x9c3: 0x0333, 0x9c4: 0x037b, 0x9c5: 0x0337, + 0x9c6: 0x033b, 0x9c7: 0x033f, 0x9c8: 0x0343, 0x9c9: 0x0347, 0x9ca: 0x034b, 0x9cb: 0x034f, + 0x9cc: 0x0353, 0x9cd: 0x0357, 0x9ce: 0x035b, 0x9cf: 0x42d8, 0x9d0: 0x42dd, 0x9d1: 0x42e2, + 0x9d2: 0x42e7, 0x9d3: 0x42ec, 0x9d4: 0x42f1, 0x9d5: 0x42f6, 0x9d6: 0x42fb, 0x9d7: 0x4300, + 0x9d8: 0x4305, 0x9d9: 0x430a, 0x9da: 0x430f, 0x9db: 0x4314, 0x9dc: 0x4319, 0x9dd: 0x431e, + 0x9de: 0x4323, 0x9df: 0x4328, 0x9e0: 0x432d, 0x9e1: 0x4332, 0x9e2: 0x4337, 0x9e3: 0x433c, + 0x9e4: 0x03c3, 0x9e5: 0x035f, 0x9e6: 0x0363, 0x9e7: 0x03e7, 0x9e8: 0x03eb, 0x9e9: 0x03ef, + 0x9ea: 0x03f3, 0x9eb: 0x03f7, 0x9ec: 0x03fb, 0x9ed: 0x03ff, 0x9ee: 0x036b, 0x9ef: 0x0403, + 0x9f0: 0x0407, 0x9f1: 0x036f, 0x9f2: 0x0373, 0x9f3: 0x0377, 0x9f4: 0x037f, 0x9f5: 0x0383, + 0x9f6: 0x0387, 0x9f7: 0x038b, 0x9f8: 0x038f, 0x9f9: 0x0393, 0x9fa: 0x0397, 0x9fb: 0x039b, + 0x9fc: 0x039f, 0x9fd: 0x03a3, 0x9fe: 0x03a7, 0x9ff: 0x03ab, + // Block 0x28, offset 0xa00 + 0xa00: 0x03af, 0xa01: 0x03b3, 0xa02: 0x040b, 0xa03: 0x040f, 0xa04: 0x03b7, 0xa05: 0x03bb, + 0xa06: 0x03bf, 0xa07: 0x03c7, 0xa08: 0x03cb, 0xa09: 0x03cf, 0xa0a: 0x03d3, 0xa0b: 0x03d7, + 0xa0c: 0x03db, 0xa0d: 0x03df, 0xa0e: 0x03e3, + 0xa12: 0x06bf, 0xa13: 0x071b, 0xa14: 0x06cb, 0xa15: 0x097b, 0xa16: 0x06cf, 0xa17: 0x06e7, + 0xa18: 0x06d3, 0xa19: 0x0f93, 0xa1a: 0x0707, 0xa1b: 0x06db, 0xa1c: 0x06c3, 0xa1d: 0x09ff, + 0xa1e: 0x098f, 0xa1f: 0x072f, + // Block 0x29, offset 0xa40 + 0xa40: 0x2050, 0xa41: 0x2056, 0xa42: 0x205c, 0xa43: 0x2062, 0xa44: 0x2068, 0xa45: 0x206e, + 0xa46: 0x2074, 0xa47: 0x207a, 0xa48: 0x2080, 0xa49: 0x2086, 0xa4a: 0x208c, 0xa4b: 0x2092, + 0xa4c: 0x2098, 0xa4d: 0x209e, 0xa4e: 0x2722, 0xa4f: 0x272b, 0xa50: 0x2734, 0xa51: 0x273d, + 0xa52: 0x2746, 0xa53: 0x274f, 0xa54: 0x2758, 0xa55: 0x2761, 0xa56: 0x276a, 0xa57: 0x277c, + 0xa58: 0x2785, 0xa59: 0x278e, 0xa5a: 0x2797, 0xa5b: 0x27a0, 0xa5c: 0x2773, 0xa5d: 0x2ba8, + 0xa5e: 0x2ae9, 0xa60: 0x20a4, 0xa61: 0x20bc, 0xa62: 0x20b0, 0xa63: 0x2104, + 0xa64: 0x20c2, 0xa65: 0x20e0, 0xa66: 0x20aa, 0xa67: 0x20da, 0xa68: 0x20b6, 0xa69: 0x20ec, + 0xa6a: 0x211c, 0xa6b: 0x213a, 0xa6c: 0x2134, 0xa6d: 0x2128, 0xa6e: 0x2176, 0xa6f: 0x210a, + 0xa70: 0x2116, 0xa71: 0x212e, 0xa72: 0x2122, 0xa73: 0x214c, 0xa74: 0x20f8, 0xa75: 0x2140, + 0xa76: 0x216a, 0xa77: 0x2152, 0xa78: 0x20e6, 0xa79: 0x20c8, 0xa7a: 0x20fe, 0xa7b: 0x2110, + 0xa7c: 0x2146, 0xa7d: 0x20ce, 0xa7e: 0x2170, 0xa7f: 0x20f2, + // Block 0x2a, offset 0xa80 + 0xa80: 0x2158, 0xa81: 0x20d4, 0xa82: 0x215e, 0xa83: 0x2164, 0xa84: 0x092f, 0xa85: 0x0b03, + 0xa86: 0x0ca7, 0xa87: 0x10c7, + 0xa90: 0x1bc0, 0xa91: 0x18a5, + 0xa92: 0x18a8, 0xa93: 0x18ab, 0xa94: 0x18ae, 0xa95: 0x18b1, 0xa96: 0x18b4, 0xa97: 0x18b7, + 0xa98: 0x18ba, 0xa99: 0x18bd, 0xa9a: 0x18c6, 0xa9b: 0x18c9, 0xa9c: 0x18cc, 0xa9d: 0x18cf, + 0xa9e: 0x18d2, 0xa9f: 0x18d5, 0xaa0: 0x0313, 0xaa1: 0x031b, 0xaa2: 0x031f, 0xaa3: 0x0327, + 0xaa4: 0x032b, 0xaa5: 0x032f, 0xaa6: 0x0337, 0xaa7: 0x033f, 0xaa8: 0x0343, 0xaa9: 0x034b, + 0xaaa: 0x034f, 0xaab: 0x0353, 0xaac: 0x0357, 0xaad: 0x035b, 0xaae: 0x2e14, 0xaaf: 0x2e1c, + 0xab0: 0x2e24, 0xab1: 0x2e2c, 0xab2: 0x2e34, 0xab3: 0x2e3c, 0xab4: 0x2e44, 0xab5: 0x2e4c, + 0xab6: 0x2e5c, 0xab7: 0x2e64, 0xab8: 0x2e6c, 0xab9: 0x2e74, 0xaba: 0x2e7c, 0xabb: 0x2e84, + 0xabc: 0x2ecf, 0xabd: 0x2e97, 0xabe: 0x2e54, + // Block 0x2b, offset 0xac0 + 0xac0: 0x06bf, 0xac1: 0x071b, 0xac2: 0x06cb, 0xac3: 0x097b, 0xac4: 0x071f, 0xac5: 0x07af, + 0xac6: 0x06c7, 0xac7: 0x07ab, 0xac8: 0x070b, 0xac9: 0x0887, 0xaca: 0x0d07, 0xacb: 0x0e8f, + 0xacc: 0x0dd7, 0xacd: 0x0d1b, 0xace: 0x145b, 0xacf: 0x098b, 0xad0: 0x0ccf, 0xad1: 0x0d4b, + 0xad2: 0x0d0b, 0xad3: 0x104b, 0xad4: 0x08fb, 0xad5: 0x0f03, 0xad6: 0x1387, 0xad7: 0x105f, + 0xad8: 0x0843, 0xad9: 0x108f, 0xada: 0x0f9b, 0xadb: 0x0a17, 0xadc: 0x140f, 0xadd: 0x077f, + 0xade: 0x08ab, 0xadf: 0x0df7, 0xae0: 0x1523, 0xae1: 0x0743, 0xae2: 0x07d3, 0xae3: 0x0d9b, + 0xae4: 0x06cf, 0xae5: 0x06e7, 0xae6: 0x06d3, 0xae7: 0x0adb, 0xae8: 0x08ef, 0xae9: 0x087f, + 0xaea: 0x0a57, 0xaeb: 0x0a4b, 0xaec: 0x0feb, 0xaed: 0x073f, 0xaee: 0x139b, 0xaef: 0x089b, + 0xaf0: 0x09f3, 0xaf1: 0x18d8, 0xaf2: 0x18db, 0xaf3: 0x18de, 0xaf4: 0x18e1, 0xaf5: 0x18ea, + 0xaf6: 0x18ed, 0xaf7: 0x18f0, 0xaf8: 0x18f3, 0xaf9: 0x18f6, 0xafa: 0x18f9, 0xafb: 0x18fc, + 0xafc: 0x18ff, 0xafd: 0x1902, 0xafe: 0x1905, 0xaff: 0x190e, + // Block 0x2c, offset 0xb00 + 0xb00: 0x1cc2, 0xb01: 0x1cd1, 0xb02: 0x1ce0, 0xb03: 0x1cef, 0xb04: 0x1cfe, 0xb05: 0x1d0d, + 0xb06: 0x1d1c, 0xb07: 0x1d2b, 0xb08: 0x1d3a, 0xb09: 0x2188, 0xb0a: 0x219a, 0xb0b: 0x21ac, + 0xb0c: 0x1950, 0xb0d: 0x1c00, 0xb0e: 0x19ce, 0xb0f: 0x1ba4, 0xb10: 0x04cb, 0xb11: 0x04d3, + 0xb12: 0x04db, 0xb13: 0x04e3, 0xb14: 0x04eb, 0xb15: 0x04ef, 0xb16: 0x04f3, 0xb17: 0x04f7, + 0xb18: 0x04fb, 0xb19: 0x04ff, 0xb1a: 0x0503, 0xb1b: 0x0507, 0xb1c: 0x050b, 0xb1d: 0x050f, + 0xb1e: 0x0513, 0xb1f: 0x0517, 0xb20: 0x051b, 0xb21: 0x0523, 0xb22: 0x0527, 0xb23: 0x052b, + 0xb24: 0x052f, 0xb25: 0x0533, 0xb26: 0x0537, 0xb27: 0x053b, 0xb28: 0x053f, 0xb29: 0x0543, + 0xb2a: 0x0547, 0xb2b: 0x054b, 0xb2c: 0x054f, 0xb2d: 0x0553, 0xb2e: 0x0557, 0xb2f: 0x055b, + 0xb30: 0x055f, 0xb31: 0x0563, 0xb32: 0x0567, 0xb33: 0x056f, 0xb34: 0x0577, 0xb35: 0x057f, + 0xb36: 0x0583, 0xb37: 0x0587, 0xb38: 0x058b, 0xb39: 0x058f, 0xb3a: 0x0593, 0xb3b: 0x0597, + 0xb3c: 0x059b, 0xb3d: 0x059f, 0xb3e: 0x05a3, + // Block 0x2d, offset 0xb40 + 0xb40: 0x2b08, 0xb41: 0x29a4, 0xb42: 0x2b18, 0xb43: 0x287c, 0xb44: 0x2ee0, 0xb45: 0x2886, + 0xb46: 0x2890, 0xb47: 0x2f24, 0xb48: 0x29b1, 0xb49: 0x289a, 0xb4a: 0x28a4, 0xb4b: 0x28ae, + 0xb4c: 0x29d8, 0xb4d: 0x29e5, 0xb4e: 0x29be, 0xb4f: 0x29cb, 0xb50: 0x2ea5, 0xb51: 0x29f2, + 0xb52: 0x29ff, 0xb53: 0x2bba, 0xb54: 0x26b7, 0xb55: 0x2bcd, 0xb56: 0x2be0, 0xb57: 0x2b28, + 0xb58: 0x2a0c, 0xb59: 0x2bf3, 0xb5a: 0x2c06, 0xb5b: 0x2a19, 0xb5c: 0x28b8, 0xb5d: 0x28c2, + 0xb5e: 0x2eb3, 0xb5f: 0x2a26, 0xb60: 0x2b38, 0xb61: 0x2ef1, 0xb62: 0x28cc, 0xb63: 0x28d6, + 0xb64: 0x2a33, 0xb65: 0x28e0, 0xb66: 0x28ea, 0xb67: 0x26cc, 0xb68: 0x26d3, 0xb69: 0x28f4, + 0xb6a: 0x28fe, 0xb6b: 0x2c19, 0xb6c: 0x2a40, 0xb6d: 0x2b48, 0xb6e: 0x2c2c, 0xb6f: 0x2a4d, + 0xb70: 0x2912, 0xb71: 0x2908, 0xb72: 0x2f38, 0xb73: 0x2a5a, 0xb74: 0x2c3f, 0xb75: 0x291c, + 0xb76: 0x2b58, 0xb77: 0x2926, 0xb78: 0x2a74, 0xb79: 0x2930, 0xb7a: 0x2a81, 0xb7b: 0x2f02, + 0xb7c: 0x2a67, 0xb7d: 0x2b68, 0xb7e: 0x2a8e, 0xb7f: 0x26da, + // Block 0x2e, offset 0xb80 + 0xb80: 0x2f13, 0xb81: 0x293a, 0xb82: 0x2944, 0xb83: 0x2a9b, 0xb84: 0x294e, 0xb85: 0x2958, + 0xb86: 0x2962, 0xb87: 0x2b78, 0xb88: 0x2aa8, 0xb89: 0x26e1, 0xb8a: 0x2c52, 0xb8b: 0x2e8c, + 0xb8c: 0x2b88, 0xb8d: 0x2ab5, 0xb8e: 0x2ec1, 0xb8f: 0x296c, 0xb90: 0x2976, 0xb91: 0x2ac2, + 0xb92: 0x26e8, 0xb93: 0x2acf, 0xb94: 0x2b98, 0xb95: 0x26ef, 0xb96: 0x2c65, 0xb97: 0x2980, + 0xb98: 0x1cb3, 0xb99: 0x1cc7, 0xb9a: 0x1cd6, 0xb9b: 0x1ce5, 0xb9c: 0x1cf4, 0xb9d: 0x1d03, + 0xb9e: 0x1d12, 0xb9f: 0x1d21, 0xba0: 0x1d30, 0xba1: 0x1d3f, 0xba2: 0x218e, 0xba3: 0x21a0, + 0xba4: 0x21b2, 0xba5: 0x21be, 0xba6: 0x21ca, 0xba7: 0x21d6, 0xba8: 0x21e2, 0xba9: 0x21ee, + 0xbaa: 0x21fa, 0xbab: 0x2206, 0xbac: 0x2242, 0xbad: 0x224e, 0xbae: 0x225a, 0xbaf: 0x2266, + 0xbb0: 0x2272, 0xbb1: 0x1c10, 0xbb2: 0x19c2, 0xbb3: 0x1932, 0xbb4: 0x1be0, 0xbb5: 0x1a43, + 0xbb6: 0x1a52, 0xbb7: 0x19c8, 0xbb8: 0x1bf8, 0xbb9: 0x1bfc, 0xbba: 0x195c, 0xbbb: 0x26fd, + 0xbbc: 0x270b, 0xbbd: 0x26f6, 0xbbe: 0x2704, 0xbbf: 0x2adc, + // Block 0x2f, offset 0xbc0 + 0xbc0: 0x1a46, 0xbc1: 0x1a2e, 0xbc2: 0x1c5c, 0xbc3: 0x1a16, 0xbc4: 0x19ef, 0xbc5: 0x1965, + 0xbc6: 0x1974, 0xbc7: 0x1944, 0xbc8: 0x1bec, 0xbc9: 0x1d4e, 0xbca: 0x1a49, 0xbcb: 0x1a31, + 0xbcc: 0x1c60, 0xbcd: 0x1c6c, 0xbce: 0x1a22, 0xbcf: 0x19f8, 0xbd0: 0x1953, 0xbd1: 0x1c18, + 0xbd2: 0x1bac, 0xbd3: 0x1b98, 0xbd4: 0x1bc8, 0xbd5: 0x1c70, 0xbd6: 0x1a25, 0xbd7: 0x19c5, + 0xbd8: 0x19fb, 0xbd9: 0x19da, 0xbda: 0x1a3d, 0xbdb: 0x1c74, 0xbdc: 0x1a28, 0xbdd: 0x19bc, + 0xbde: 0x19fe, 0xbdf: 0x1c38, 0xbe0: 0x1bf0, 0xbe1: 0x1a10, 0xbe2: 0x1c20, 0xbe3: 0x1c3c, + 0xbe4: 0x1bf4, 0xbe5: 0x1a13, 0xbe6: 0x1c24, 0xbe7: 0x22e4, 0xbe8: 0x22f8, 0xbe9: 0x1992, + 0xbea: 0x1c1c, 0xbeb: 0x1bb0, 0xbec: 0x1b9c, 0xbed: 0x1c44, 0xbee: 0x2712, 0xbef: 0x27a9, + 0xbf0: 0x1a55, 0xbf1: 0x1a40, 0xbf2: 0x1c78, 0xbf3: 0x1a2b, 0xbf4: 0x1a4c, 0xbf5: 0x1a34, + 0xbf6: 0x1c64, 0xbf7: 0x1a19, 0xbf8: 0x19f2, 0xbf9: 0x197d, 0xbfa: 0x1a4f, 0xbfb: 0x1a37, + 0xbfc: 0x1c68, 0xbfd: 0x1a1c, 0xbfe: 0x19f5, 0xbff: 0x1980, + // Block 0x30, offset 0xc00 + 0xc00: 0x1c28, 0xc01: 0x1bb4, 0xc02: 0x1d49, 0xc03: 0x1935, 0xc04: 0x19b6, 0xc05: 0x19b9, + 0xc06: 0x22f1, 0xc07: 0x1b90, 0xc08: 0x19bf, 0xc09: 0x1947, 0xc0a: 0x19dd, 0xc0b: 0x194a, + 0xc0c: 0x19e6, 0xc0d: 0x1968, 0xc0e: 0x196b, 0xc0f: 0x1a01, 0xc10: 0x1a07, 0xc11: 0x1a0a, + 0xc12: 0x1c2c, 0xc13: 0x1a0d, 0xc14: 0x1a1f, 0xc15: 0x1c34, 0xc16: 0x1c40, 0xc17: 0x198c, + 0xc18: 0x1d53, 0xc19: 0x1bb8, 0xc1a: 0x198f, 0xc1b: 0x1a58, 0xc1c: 0x19a1, 0xc1d: 0x19b0, + 0xc1e: 0x22de, 0xc1f: 0x22d8, 0xc20: 0x1cbd, 0xc21: 0x1ccc, 0xc22: 0x1cdb, 0xc23: 0x1cea, + 0xc24: 0x1cf9, 0xc25: 0x1d08, 0xc26: 0x1d17, 0xc27: 0x1d26, 0xc28: 0x1d35, 0xc29: 0x2182, + 0xc2a: 0x2194, 0xc2b: 0x21a6, 0xc2c: 0x21b8, 0xc2d: 0x21c4, 0xc2e: 0x21d0, 0xc2f: 0x21dc, + 0xc30: 0x21e8, 0xc31: 0x21f4, 0xc32: 0x2200, 0xc33: 0x223c, 0xc34: 0x2248, 0xc35: 0x2254, + 0xc36: 0x2260, 0xc37: 0x226c, 0xc38: 0x2278, 0xc39: 0x227e, 0xc3a: 0x2284, 0xc3b: 0x228a, + 0xc3c: 0x2290, 0xc3d: 0x22a2, 0xc3e: 0x22a8, 0xc3f: 0x1c0c, + // Block 0x31, offset 0xc40 + 0xc40: 0x1377, 0xc41: 0x0cfb, 0xc42: 0x13d3, 0xc43: 0x139f, 0xc44: 0x0e57, 0xc45: 0x06eb, + 0xc46: 0x08df, 0xc47: 0x1627, 0xc48: 0x1627, 0xc49: 0x0a0b, 0xc4a: 0x145b, 0xc4b: 0x0943, + 0xc4c: 0x0a07, 0xc4d: 0x0bef, 0xc4e: 0x0fcf, 0xc4f: 0x115f, 0xc50: 0x1297, 0xc51: 0x12d3, + 0xc52: 0x1307, 0xc53: 0x141b, 0xc54: 0x0d73, 0xc55: 0x0dff, 0xc56: 0x0eab, 0xc57: 0x0f43, + 0xc58: 0x125f, 0xc59: 0x1443, 0xc5a: 0x156f, 0xc5b: 0x070f, 0xc5c: 0x08b3, 0xc5d: 0x0d87, + 0xc5e: 0x0ecf, 0xc5f: 0x1293, 0xc60: 0x15bf, 0xc61: 0x0ab3, 0xc62: 0x0e77, 0xc63: 0x1283, + 0xc64: 0x1317, 0xc65: 0x0c23, 0xc66: 0x11bb, 0xc67: 0x12df, 0xc68: 0x0b1f, 0xc69: 0x0d0f, + 0xc6a: 0x0e17, 0xc6b: 0x0f1b, 0xc6c: 0x1427, 0xc6d: 0x074f, 0xc6e: 0x07e7, 0xc6f: 0x0853, + 0xc70: 0x0c8b, 0xc71: 0x0d7f, 0xc72: 0x0ecb, 0xc73: 0x0fef, 0xc74: 0x1177, 0xc75: 0x128b, + 0xc76: 0x12a3, 0xc77: 0x13c7, 0xc78: 0x14eb, 0xc79: 0x159f, 0xc7a: 0x15bb, 0xc7b: 0x102b, + 0xc7c: 0x106b, 0xc7d: 0x1123, 0xc7e: 0x1243, 0xc7f: 0x1477, + // Block 0x32, offset 0xc80 + 0xc80: 0x15c7, 0xc81: 0x134b, 0xc82: 0x09c7, 0xc83: 0x0b3b, 0xc84: 0x10db, 0xc85: 0x119b, + 0xc86: 0x0eff, 0xc87: 0x1033, 0xc88: 0x1397, 0xc89: 0x14e3, 0xc8a: 0x09c3, 0xc8b: 0x0a8f, + 0xc8c: 0x0d77, 0xc8d: 0x0e2b, 0xc8e: 0x0e5f, 0xc8f: 0x1113, 0xc90: 0x113b, 0xc91: 0x14a3, + 0xc92: 0x084f, 0xc93: 0x11a7, 0xc94: 0x07f3, 0xc95: 0x07ef, 0xc96: 0x1097, 0xc97: 0x1127, + 0xc98: 0x125b, 0xc99: 0x14ab, 0xc9a: 0x1367, 0xc9b: 0x0c27, 0xc9c: 0x0d73, 0xc9d: 0x1357, + 0xc9e: 0x06f7, 0xc9f: 0x0a63, 0xca0: 0x0b93, 0xca1: 0x0f2f, 0xca2: 0x0faf, 0xca3: 0x0873, + 0xca4: 0x103b, 0xca5: 0x075f, 0xca6: 0x0b77, 0xca7: 0x06d7, 0xca8: 0x0deb, 0xca9: 0x0ca3, + 0xcaa: 0x110f, 0xcab: 0x08c7, 0xcac: 0x09b3, 0xcad: 0x0ffb, 0xcae: 0x1263, 0xcaf: 0x133b, + 0xcb0: 0x0db7, 0xcb1: 0x13f7, 0xcb2: 0x0de3, 0xcb3: 0x0c37, 0xcb4: 0x121b, 0xcb5: 0x0c57, + 0xcb6: 0x0fab, 0xcb7: 0x072b, 0xcb8: 0x07a7, 0xcb9: 0x07eb, 0xcba: 0x0d53, 0xcbb: 0x10fb, + 0xcbc: 0x11f3, 0xcbd: 0x1347, 0xcbe: 0x1457, 0xcbf: 0x085b, + // Block 0x33, offset 0xcc0 + 0xcc0: 0x090f, 0xcc1: 0x0a17, 0xcc2: 0x0b2f, 0xcc3: 0x0cbf, 0xcc4: 0x0e7b, 0xcc5: 0x103f, + 0xcc6: 0x1493, 0xcc7: 0x1577, 0xcc8: 0x15cb, 0xcc9: 0x15e3, 0xcca: 0x0837, 0xccb: 0x0cf3, + 0xccc: 0x0da3, 0xccd: 0x13eb, 0xcce: 0x0afb, 0xccf: 0x0bd7, 0xcd0: 0x0bf3, 0xcd1: 0x0c83, + 0xcd2: 0x0e6b, 0xcd3: 0x0eb7, 0xcd4: 0x0f67, 0xcd5: 0x108b, 0xcd6: 0x112f, 0xcd7: 0x1193, + 0xcd8: 0x13db, 0xcd9: 0x126b, 0xcda: 0x1403, 0xcdb: 0x147b, 0xcdc: 0x080f, 0xcdd: 0x083b, + 0xcde: 0x0923, 0xcdf: 0x0ea7, 0xce0: 0x12f3, 0xce1: 0x133b, 0xce2: 0x0b1b, 0xce3: 0x0b8b, + 0xce4: 0x0c4f, 0xce5: 0x0daf, 0xce6: 0x10d7, 0xce7: 0x0f23, 0xce8: 0x073b, 0xce9: 0x097f, + 0xcea: 0x0a63, 0xceb: 0x0ac7, 0xcec: 0x0b97, 0xced: 0x0f3f, 0xcee: 0x0f5b, 0xcef: 0x116b, + 0xcf0: 0x118b, 0xcf1: 0x145f, 0xcf2: 0x14df, 0xcf3: 0x14ef, 0xcf4: 0x152b, 0xcf5: 0x0753, + 0xcf6: 0x107f, 0xcf7: 0x144b, 0xcf8: 0x14c7, 0xcf9: 0x0baf, 0xcfa: 0x0717, 0xcfb: 0x0777, + 0xcfc: 0x0a67, 0xcfd: 0x0a87, 0xcfe: 0x0caf, 0xcff: 0x0d73, + // Block 0x34, offset 0xd00 + 0xd00: 0x0ec3, 0xd01: 0x0fcb, 0xd02: 0x1277, 0xd03: 0x1417, 0xd04: 0x161f, 0xd05: 0x0ce3, + 0xd06: 0x149f, 0xd07: 0x0833, 0xd08: 0x0d2f, 0xd09: 0x0d3b, 0xd0a: 0x0e0f, 0xd0b: 0x0e47, + 0xd0c: 0x0f4b, 0xd0d: 0x0fa7, 0xd0e: 0x1027, 0xd0f: 0x110b, 0xd10: 0x1537, 0xd11: 0x07af, + 0xd12: 0x0c03, 0xd13: 0x14af, 0xd14: 0x0767, 0xd15: 0x0aab, 0xd16: 0x0e2f, 0xd17: 0x13df, + 0xd18: 0x0b67, 0xd19: 0x0bb7, 0xd1a: 0x0d43, 0xd1b: 0x0f2f, 0xd1c: 0x14b7, 0xd1d: 0x0817, + 0xd1e: 0x08ff, 0xd1f: 0x0a97, 0xd20: 0x0cd3, 0xd21: 0x0d1f, 0xd22: 0x0d5f, 0xd23: 0x0df3, + 0xd24: 0x0f47, 0xd25: 0x0fbb, 0xd26: 0x1157, 0xd27: 0x12f7, 0xd28: 0x1303, 0xd29: 0x1453, + 0xd2a: 0x14d3, 0xd2b: 0x0883, 0xd2c: 0x0e4b, 0xd2d: 0x0903, 0xd2e: 0x0ec7, 0xd2f: 0x0f6b, + 0xd30: 0x1287, 0xd31: 0x14bb, 0xd32: 0x15a7, 0xd33: 0x15cf, 0xd34: 0x0d37, 0xd35: 0x0e27, + 0xd36: 0x11c3, 0xd37: 0x10b7, 0xd38: 0x10c3, 0xd39: 0x10e7, 0xd3a: 0x0f17, 0xd3b: 0x0e9f, + 0xd3c: 0x1363, 0xd3d: 0x0733, 0xd3e: 0x122b, 0xd3f: 0x081b, + // Block 0x35, offset 0xd40 + 0xd40: 0x080b, 0xd41: 0x0b0b, 0xd42: 0x0c2b, 0xd43: 0x10f3, 0xd44: 0x0a53, 0xd45: 0x0e03, + 0xd46: 0x0cef, 0xd47: 0x13e7, 0xd48: 0x12e7, 0xd49: 0x14a7, 0xd4a: 0x1323, 0xd4b: 0x0b27, + 0xd4c: 0x0787, 0xd4d: 0x095b, 0xd50: 0x09af, + 0xd52: 0x0cdf, 0xd55: 0x07f7, 0xd56: 0x0f1f, 0xd57: 0x0fe3, + 0xd58: 0x1047, 0xd59: 0x1063, 0xd5a: 0x1067, 0xd5b: 0x107b, 0xd5c: 0x14f7, 0xd5d: 0x10eb, + 0xd5e: 0x116f, 0xd60: 0x128f, 0xd62: 0x1353, + 0xd65: 0x1407, 0xd66: 0x1433, + 0xd6a: 0x154b, 0xd6b: 0x154f, 0xd6c: 0x1553, 0xd6d: 0x15b7, 0xd6e: 0x142b, 0xd6f: 0x14c3, + 0xd70: 0x0757, 0xd71: 0x077b, 0xd72: 0x078f, 0xd73: 0x084b, 0xd74: 0x0857, 0xd75: 0x0897, + 0xd76: 0x094b, 0xd77: 0x0967, 0xd78: 0x096f, 0xd79: 0x09ab, 0xd7a: 0x09b7, 0xd7b: 0x0a93, + 0xd7c: 0x0a9b, 0xd7d: 0x0ba3, 0xd7e: 0x0bcb, 0xd7f: 0x0bd3, + // Block 0x36, offset 0xd80 + 0xd80: 0x0beb, 0xd81: 0x0c97, 0xd82: 0x0cc7, 0xd83: 0x0ce7, 0xd84: 0x0d57, 0xd85: 0x0e1b, + 0xd86: 0x0e37, 0xd87: 0x0e67, 0xd88: 0x0ebb, 0xd89: 0x0edb, 0xd8a: 0x0f4f, 0xd8b: 0x102f, + 0xd8c: 0x104b, 0xd8d: 0x1053, 0xd8e: 0x104f, 0xd8f: 0x1057, 0xd90: 0x105b, 0xd91: 0x105f, + 0xd92: 0x1073, 0xd93: 0x1077, 0xd94: 0x109b, 0xd95: 0x10af, 0xd96: 0x10cb, 0xd97: 0x112f, + 0xd98: 0x1137, 0xd99: 0x113f, 0xd9a: 0x1153, 0xd9b: 0x117b, 0xd9c: 0x11cb, 0xd9d: 0x11ff, + 0xd9e: 0x11ff, 0xd9f: 0x1267, 0xda0: 0x130f, 0xda1: 0x1327, 0xda2: 0x135b, 0xda3: 0x135f, + 0xda4: 0x13a3, 0xda5: 0x13a7, 0xda6: 0x13ff, 0xda7: 0x1407, 0xda8: 0x14d7, 0xda9: 0x151b, + 0xdaa: 0x1533, 0xdab: 0x0b9b, 0xdac: 0x171a, 0xdad: 0x11e3, + 0xdb0: 0x06df, 0xdb1: 0x07e3, 0xdb2: 0x07a3, 0xdb3: 0x074b, 0xdb4: 0x078b, 0xdb5: 0x07b7, + 0xdb6: 0x0847, 0xdb7: 0x0863, 0xdb8: 0x094b, 0xdb9: 0x0937, 0xdba: 0x0947, 0xdbb: 0x0963, + 0xdbc: 0x09af, 0xdbd: 0x09bf, 0xdbe: 0x0a03, 0xdbf: 0x0a0f, + // Block 0x37, offset 0xdc0 + 0xdc0: 0x0a2b, 0xdc1: 0x0a3b, 0xdc2: 0x0b23, 0xdc3: 0x0b2b, 0xdc4: 0x0b5b, 0xdc5: 0x0b7b, + 0xdc6: 0x0bab, 0xdc7: 0x0bc3, 0xdc8: 0x0bb3, 0xdc9: 0x0bd3, 0xdca: 0x0bc7, 0xdcb: 0x0beb, + 0xdcc: 0x0c07, 0xdcd: 0x0c5f, 0xdce: 0x0c6b, 0xdcf: 0x0c73, 0xdd0: 0x0c9b, 0xdd1: 0x0cdf, + 0xdd2: 0x0d0f, 0xdd3: 0x0d13, 0xdd4: 0x0d27, 0xdd5: 0x0da7, 0xdd6: 0x0db7, 0xdd7: 0x0e0f, + 0xdd8: 0x0e5b, 0xdd9: 0x0e53, 0xdda: 0x0e67, 0xddb: 0x0e83, 0xddc: 0x0ebb, 0xddd: 0x1013, + 0xdde: 0x0edf, 0xddf: 0x0f13, 0xde0: 0x0f1f, 0xde1: 0x0f5f, 0xde2: 0x0f7b, 0xde3: 0x0f9f, + 0xde4: 0x0fc3, 0xde5: 0x0fc7, 0xde6: 0x0fe3, 0xde7: 0x0fe7, 0xde8: 0x0ff7, 0xde9: 0x100b, + 0xdea: 0x1007, 0xdeb: 0x1037, 0xdec: 0x10b3, 0xded: 0x10cb, 0xdee: 0x10e3, 0xdef: 0x111b, + 0xdf0: 0x112f, 0xdf1: 0x114b, 0xdf2: 0x117b, 0xdf3: 0x122f, 0xdf4: 0x1257, 0xdf5: 0x12cb, + 0xdf6: 0x1313, 0xdf7: 0x131f, 0xdf8: 0x1327, 0xdf9: 0x133f, 0xdfa: 0x1353, 0xdfb: 0x1343, + 0xdfc: 0x135b, 0xdfd: 0x1357, 0xdfe: 0x134f, 0xdff: 0x135f, + // Block 0x38, offset 0xe00 + 0xe00: 0x136b, 0xe01: 0x13a7, 0xe02: 0x13e3, 0xe03: 0x1413, 0xe04: 0x1447, 0xe05: 0x1467, + 0xe06: 0x14b3, 0xe07: 0x14d7, 0xe08: 0x14f7, 0xe09: 0x150b, 0xe0a: 0x151b, 0xe0b: 0x1527, + 0xe0c: 0x1533, 0xe0d: 0x1587, 0xe0e: 0x1627, 0xe0f: 0x16b1, 0xe10: 0x16ac, 0xe11: 0x16de, + 0xe12: 0x0607, 0xe13: 0x062f, 0xe14: 0x0633, 0xe15: 0x1760, 0xe16: 0x178d, 0xe17: 0x1805, + 0xe18: 0x1613, 0xe19: 0x1623, + // Block 0x39, offset 0xe40 + 0xe40: 0x19d1, 0xe41: 0x19d4, 0xe42: 0x19d7, 0xe43: 0x1c04, 0xe44: 0x1c08, 0xe45: 0x1a5b, + 0xe46: 0x1a5b, + 0xe53: 0x1d71, 0xe54: 0x1d62, 0xe55: 0x1d67, 0xe56: 0x1d76, 0xe57: 0x1d6c, + 0xe5d: 0x4422, + 0xe5e: 0x8115, 0xe5f: 0x4494, 0xe60: 0x022d, 0xe61: 0x0215, 0xe62: 0x021e, 0xe63: 0x0221, + 0xe64: 0x0224, 0xe65: 0x0227, 0xe66: 0x022a, 0xe67: 0x0230, 0xe68: 0x0233, 0xe69: 0x0017, + 0xe6a: 0x4482, 0xe6b: 0x4488, 0xe6c: 0x4586, 0xe6d: 0x458e, 0xe6e: 0x43da, 0xe6f: 0x43e0, + 0xe70: 0x43e6, 0xe71: 0x43ec, 0xe72: 0x43f8, 0xe73: 0x43fe, 0xe74: 0x4404, 0xe75: 0x4410, + 0xe76: 0x4416, 0xe78: 0x441c, 0xe79: 0x4428, 0xe7a: 0x442e, 0xe7b: 0x4434, + 0xe7c: 0x4440, 0xe7e: 0x4446, + // Block 0x3a, offset 0xe80 + 0xe80: 0x444c, 0xe81: 0x4452, 0xe83: 0x4458, 0xe84: 0x445e, + 0xe86: 0x446a, 0xe87: 0x4470, 0xe88: 0x4476, 0xe89: 0x447c, 0xe8a: 0x448e, 0xe8b: 0x440a, + 0xe8c: 0x43f2, 0xe8d: 0x443a, 0xe8e: 0x4464, 0xe8f: 0x1d7b, 0xe90: 0x0299, 0xe91: 0x0299, + 0xe92: 0x02a2, 0xe93: 0x02a2, 0xe94: 0x02a2, 0xe95: 0x02a2, 0xe96: 0x02a5, 0xe97: 0x02a5, + 0xe98: 0x02a5, 0xe99: 0x02a5, 0xe9a: 0x02ab, 0xe9b: 0x02ab, 0xe9c: 0x02ab, 0xe9d: 0x02ab, + 0xe9e: 0x029f, 0xe9f: 0x029f, 0xea0: 0x029f, 0xea1: 0x029f, 0xea2: 0x02a8, 0xea3: 0x02a8, + 0xea4: 0x02a8, 0xea5: 0x02a8, 0xea6: 0x029c, 0xea7: 0x029c, 0xea8: 0x029c, 0xea9: 0x029c, + 0xeaa: 0x02cf, 0xeab: 0x02cf, 0xeac: 0x02cf, 0xead: 0x02cf, 0xeae: 0x02d2, 0xeaf: 0x02d2, + 0xeb0: 0x02d2, 0xeb1: 0x02d2, 0xeb2: 0x02b1, 0xeb3: 0x02b1, 0xeb4: 0x02b1, 0xeb5: 0x02b1, + 0xeb6: 0x02ae, 0xeb7: 0x02ae, 0xeb8: 0x02ae, 0xeb9: 0x02ae, 0xeba: 0x02b4, 0xebb: 0x02b4, + 0xebc: 0x02b4, 0xebd: 0x02b4, 0xebe: 0x02b7, 0xebf: 0x02b7, + // Block 0x3b, offset 0xec0 + 0xec0: 0x02b7, 0xec1: 0x02b7, 0xec2: 0x02c0, 0xec3: 0x02c0, 0xec4: 0x02bd, 0xec5: 0x02bd, + 0xec6: 0x02c3, 0xec7: 0x02c3, 0xec8: 0x02ba, 0xec9: 0x02ba, 0xeca: 0x02c9, 0xecb: 0x02c9, + 0xecc: 0x02c6, 0xecd: 0x02c6, 0xece: 0x02d5, 0xecf: 0x02d5, 0xed0: 0x02d5, 0xed1: 0x02d5, + 0xed2: 0x02db, 0xed3: 0x02db, 0xed4: 0x02db, 0xed5: 0x02db, 0xed6: 0x02e1, 0xed7: 0x02e1, + 0xed8: 0x02e1, 0xed9: 0x02e1, 0xeda: 0x02de, 0xedb: 0x02de, 0xedc: 0x02de, 0xedd: 0x02de, + 0xede: 0x02e4, 0xedf: 0x02e4, 0xee0: 0x02e7, 0xee1: 0x02e7, 0xee2: 0x02e7, 0xee3: 0x02e7, + 0xee4: 0x4500, 0xee5: 0x4500, 0xee6: 0x02ed, 0xee7: 0x02ed, 0xee8: 0x02ed, 0xee9: 0x02ed, + 0xeea: 0x02ea, 0xeeb: 0x02ea, 0xeec: 0x02ea, 0xeed: 0x02ea, 0xeee: 0x0308, 0xeef: 0x0308, + 0xef0: 0x44fa, 0xef1: 0x44fa, + // Block 0x3c, offset 0xf00 + 0xf13: 0x02d8, 0xf14: 0x02d8, 0xf15: 0x02d8, 0xf16: 0x02d8, 0xf17: 0x02f6, + 0xf18: 0x02f6, 0xf19: 0x02f3, 0xf1a: 0x02f3, 0xf1b: 0x02f9, 0xf1c: 0x02f9, 0xf1d: 0x204b, + 0xf1e: 0x02ff, 0xf1f: 0x02ff, 0xf20: 0x02f0, 0xf21: 0x02f0, 0xf22: 0x02fc, 0xf23: 0x02fc, + 0xf24: 0x0305, 0xf25: 0x0305, 0xf26: 0x0305, 0xf27: 0x0305, 0xf28: 0x028d, 0xf29: 0x028d, + 0xf2a: 0x25a6, 0xf2b: 0x25a6, 0xf2c: 0x2616, 0xf2d: 0x2616, 0xf2e: 0x25e5, 0xf2f: 0x25e5, + 0xf30: 0x2601, 0xf31: 0x2601, 0xf32: 0x25fa, 0xf33: 0x25fa, 0xf34: 0x2608, 0xf35: 0x2608, + 0xf36: 0x260f, 0xf37: 0x260f, 0xf38: 0x260f, 0xf39: 0x25ec, 0xf3a: 0x25ec, 0xf3b: 0x25ec, + 0xf3c: 0x0302, 0xf3d: 0x0302, 0xf3e: 0x0302, 0xf3f: 0x0302, + // Block 0x3d, offset 0xf40 + 0xf40: 0x25ad, 0xf41: 0x25b4, 0xf42: 0x25d0, 0xf43: 0x25ec, 0xf44: 0x25f3, 0xf45: 0x1d85, + 0xf46: 0x1d8a, 0xf47: 0x1d8f, 0xf48: 0x1d9e, 0xf49: 0x1dad, 0xf4a: 0x1db2, 0xf4b: 0x1db7, + 0xf4c: 0x1dbc, 0xf4d: 0x1dc1, 0xf4e: 0x1dd0, 0xf4f: 0x1ddf, 0xf50: 0x1de4, 0xf51: 0x1de9, + 0xf52: 0x1df8, 0xf53: 0x1e07, 0xf54: 0x1e0c, 0xf55: 0x1e11, 0xf56: 0x1e16, 0xf57: 0x1e25, + 0xf58: 0x1e2a, 0xf59: 0x1e39, 0xf5a: 0x1e3e, 0xf5b: 0x1e43, 0xf5c: 0x1e52, 0xf5d: 0x1e57, + 0xf5e: 0x1e5c, 0xf5f: 0x1e66, 0xf60: 0x1ea2, 0xf61: 0x1eb1, 0xf62: 0x1ec0, 0xf63: 0x1ec5, + 0xf64: 0x1eca, 0xf65: 0x1ed4, 0xf66: 0x1ee3, 0xf67: 0x1ee8, 0xf68: 0x1ef7, 0xf69: 0x1efc, + 0xf6a: 0x1f01, 0xf6b: 0x1f10, 0xf6c: 0x1f15, 0xf6d: 0x1f24, 0xf6e: 0x1f29, 0xf6f: 0x1f2e, + 0xf70: 0x1f33, 0xf71: 0x1f38, 0xf72: 0x1f3d, 0xf73: 0x1f42, 0xf74: 0x1f47, 0xf75: 0x1f4c, + 0xf76: 0x1f51, 0xf77: 0x1f56, 0xf78: 0x1f5b, 0xf79: 0x1f60, 0xf7a: 0x1f65, 0xf7b: 0x1f6a, + 0xf7c: 0x1f6f, 0xf7d: 0x1f74, 0xf7e: 0x1f79, 0xf7f: 0x1f83, + // Block 0x3e, offset 0xf80 + 0xf80: 0x1f88, 0xf81: 0x1f8d, 0xf82: 0x1f92, 0xf83: 0x1f9c, 0xf84: 0x1fa1, 0xf85: 0x1fab, + 0xf86: 0x1fb0, 0xf87: 0x1fb5, 0xf88: 0x1fba, 0xf89: 0x1fbf, 0xf8a: 0x1fc4, 0xf8b: 0x1fc9, + 0xf8c: 0x1fce, 0xf8d: 0x1fd3, 0xf8e: 0x1fe2, 0xf8f: 0x1ff1, 0xf90: 0x1ff6, 0xf91: 0x1ffb, + 0xf92: 0x2000, 0xf93: 0x2005, 0xf94: 0x200a, 0xf95: 0x2014, 0xf96: 0x2019, 0xf97: 0x201e, + 0xf98: 0x202d, 0xf99: 0x203c, 0xf9a: 0x2041, 0xf9b: 0x44b2, 0xf9c: 0x44b8, 0xf9d: 0x44ee, + 0xf9e: 0x4545, 0xf9f: 0x454c, 0xfa0: 0x4553, 0xfa1: 0x455a, 0xfa2: 0x4561, 0xfa3: 0x4568, + 0xfa4: 0x25c2, 0xfa5: 0x25c9, 0xfa6: 0x25d0, 0xfa7: 0x25d7, 0xfa8: 0x25ec, 0xfa9: 0x25f3, + 0xfaa: 0x1d94, 0xfab: 0x1d99, 0xfac: 0x1d9e, 0xfad: 0x1da3, 0xfae: 0x1dad, 0xfaf: 0x1db2, + 0xfb0: 0x1dc6, 0xfb1: 0x1dcb, 0xfb2: 0x1dd0, 0xfb3: 0x1dd5, 0xfb4: 0x1ddf, 0xfb5: 0x1de4, + 0xfb6: 0x1dee, 0xfb7: 0x1df3, 0xfb8: 0x1df8, 0xfb9: 0x1dfd, 0xfba: 0x1e07, 0xfbb: 0x1e0c, + 0xfbc: 0x1f38, 0xfbd: 0x1f3d, 0xfbe: 0x1f4c, 0xfbf: 0x1f51, + // Block 0x3f, offset 0xfc0 + 0xfc0: 0x1f56, 0xfc1: 0x1f6a, 0xfc2: 0x1f6f, 0xfc3: 0x1f74, 0xfc4: 0x1f79, 0xfc5: 0x1f92, + 0xfc6: 0x1f9c, 0xfc7: 0x1fa1, 0xfc8: 0x1fa6, 0xfc9: 0x1fba, 0xfca: 0x1fd8, 0xfcb: 0x1fdd, + 0xfcc: 0x1fe2, 0xfcd: 0x1fe7, 0xfce: 0x1ff1, 0xfcf: 0x1ff6, 0xfd0: 0x44ee, 0xfd1: 0x2023, + 0xfd2: 0x2028, 0xfd3: 0x202d, 0xfd4: 0x2032, 0xfd5: 0x203c, 0xfd6: 0x2041, 0xfd7: 0x25ad, + 0xfd8: 0x25b4, 0xfd9: 0x25bb, 0xfda: 0x25d0, 0xfdb: 0x25de, 0xfdc: 0x1d85, 0xfdd: 0x1d8a, + 0xfde: 0x1d8f, 0xfdf: 0x1d9e, 0xfe0: 0x1da8, 0xfe1: 0x1db7, 0xfe2: 0x1dbc, 0xfe3: 0x1dc1, + 0xfe4: 0x1dd0, 0xfe5: 0x1dda, 0xfe6: 0x1df8, 0xfe7: 0x1e11, 0xfe8: 0x1e16, 0xfe9: 0x1e25, + 0xfea: 0x1e2a, 0xfeb: 0x1e39, 0xfec: 0x1e43, 0xfed: 0x1e52, 0xfee: 0x1e57, 0xfef: 0x1e5c, + 0xff0: 0x1e66, 0xff1: 0x1ea2, 0xff2: 0x1ea7, 0xff3: 0x1eb1, 0xff4: 0x1ec0, 0xff5: 0x1ec5, + 0xff6: 0x1eca, 0xff7: 0x1ed4, 0xff8: 0x1ee3, 0xff9: 0x1ef7, 0xffa: 0x1efc, 0xffb: 0x1f01, + 0xffc: 0x1f10, 0xffd: 0x1f15, 0xffe: 0x1f24, 0xfff: 0x1f29, + // Block 0x40, offset 0x1000 + 0x1000: 0x1f2e, 0x1001: 0x1f33, 0x1002: 0x1f42, 0x1003: 0x1f47, 0x1004: 0x1f5b, 0x1005: 0x1f60, + 0x1006: 0x1f65, 0x1007: 0x1f6a, 0x1008: 0x1f6f, 0x1009: 0x1f83, 0x100a: 0x1f88, 0x100b: 0x1f8d, + 0x100c: 0x1f92, 0x100d: 0x1f97, 0x100e: 0x1fab, 0x100f: 0x1fb0, 0x1010: 0x1fb5, 0x1011: 0x1fba, + 0x1012: 0x1fc9, 0x1013: 0x1fce, 0x1014: 0x1fd3, 0x1015: 0x1fe2, 0x1016: 0x1fec, 0x1017: 0x1ffb, + 0x1018: 0x2000, 0x1019: 0x44e2, 0x101a: 0x2014, 0x101b: 0x2019, 0x101c: 0x201e, 0x101d: 0x202d, + 0x101e: 0x2037, 0x101f: 0x25d0, 0x1020: 0x25de, 0x1021: 0x1d9e, 0x1022: 0x1da8, 0x1023: 0x1dd0, + 0x1024: 0x1dda, 0x1025: 0x1df8, 0x1026: 0x1e02, 0x1027: 0x1e66, 0x1028: 0x1e6b, 0x1029: 0x1e8e, + 0x102a: 0x1e93, 0x102b: 0x1f6a, 0x102c: 0x1f6f, 0x102d: 0x1f92, 0x102e: 0x1fe2, 0x102f: 0x1fec, + 0x1030: 0x202d, 0x1031: 0x2037, 0x1032: 0x4596, 0x1033: 0x459e, 0x1034: 0x45a6, 0x1035: 0x1eed, + 0x1036: 0x1ef2, 0x1037: 0x1f06, 0x1038: 0x1f0b, 0x1039: 0x1f1a, 0x103a: 0x1f1f, 0x103b: 0x1e70, + 0x103c: 0x1e75, 0x103d: 0x1e98, 0x103e: 0x1e9d, 0x103f: 0x1e2f, + // Block 0x41, offset 0x1040 + 0x1040: 0x1e34, 0x1041: 0x1e1b, 0x1042: 0x1e20, 0x1043: 0x1e48, 0x1044: 0x1e4d, 0x1045: 0x1eb6, + 0x1046: 0x1ebb, 0x1047: 0x1ed9, 0x1048: 0x1ede, 0x1049: 0x1e7a, 0x104a: 0x1e7f, 0x104b: 0x1e84, + 0x104c: 0x1e8e, 0x104d: 0x1e89, 0x104e: 0x1e61, 0x104f: 0x1eac, 0x1050: 0x1ecf, 0x1051: 0x1eed, + 0x1052: 0x1ef2, 0x1053: 0x1f06, 0x1054: 0x1f0b, 0x1055: 0x1f1a, 0x1056: 0x1f1f, 0x1057: 0x1e70, + 0x1058: 0x1e75, 0x1059: 0x1e98, 0x105a: 0x1e9d, 0x105b: 0x1e2f, 0x105c: 0x1e34, 0x105d: 0x1e1b, + 0x105e: 0x1e20, 0x105f: 0x1e48, 0x1060: 0x1e4d, 0x1061: 0x1eb6, 0x1062: 0x1ebb, 0x1063: 0x1ed9, + 0x1064: 0x1ede, 0x1065: 0x1e7a, 0x1066: 0x1e7f, 0x1067: 0x1e84, 0x1068: 0x1e8e, 0x1069: 0x1e89, + 0x106a: 0x1e61, 0x106b: 0x1eac, 0x106c: 0x1ecf, 0x106d: 0x1e7a, 0x106e: 0x1e7f, 0x106f: 0x1e84, + 0x1070: 0x1e8e, 0x1071: 0x1e6b, 0x1072: 0x1e93, 0x1073: 0x1ee8, 0x1074: 0x1e52, 0x1075: 0x1e57, + 0x1076: 0x1e5c, 0x1077: 0x1e7a, 0x1078: 0x1e7f, 0x1079: 0x1e84, 0x107a: 0x1ee8, 0x107b: 0x1ef7, + 0x107c: 0x449a, 0x107d: 0x449a, + // Block 0x42, offset 0x1080 + 0x1090: 0x230d, 0x1091: 0x2322, + 0x1092: 0x2322, 0x1093: 0x2329, 0x1094: 0x2330, 0x1095: 0x2345, 0x1096: 0x234c, 0x1097: 0x2353, + 0x1098: 0x2376, 0x1099: 0x2376, 0x109a: 0x2399, 0x109b: 0x2392, 0x109c: 0x23ae, 0x109d: 0x23a0, + 0x109e: 0x23a7, 0x109f: 0x23ca, 0x10a0: 0x23ca, 0x10a1: 0x23c3, 0x10a2: 0x23d1, 0x10a3: 0x23d1, + 0x10a4: 0x23fb, 0x10a5: 0x23fb, 0x10a6: 0x2417, 0x10a7: 0x23df, 0x10a8: 0x23df, 0x10a9: 0x23d8, + 0x10aa: 0x23ed, 0x10ab: 0x23ed, 0x10ac: 0x23f4, 0x10ad: 0x23f4, 0x10ae: 0x241e, 0x10af: 0x242c, + 0x10b0: 0x242c, 0x10b1: 0x2433, 0x10b2: 0x2433, 0x10b3: 0x243a, 0x10b4: 0x2441, 0x10b5: 0x2448, + 0x10b6: 0x244f, 0x10b7: 0x244f, 0x10b8: 0x2456, 0x10b9: 0x2464, 0x10ba: 0x2472, 0x10bb: 0x246b, + 0x10bc: 0x2479, 0x10bd: 0x2479, 0x10be: 0x248e, 0x10bf: 0x2495, + // Block 0x43, offset 0x10c0 + 0x10c0: 0x24c6, 0x10c1: 0x24d4, 0x10c2: 0x24cd, 0x10c3: 0x24b1, 0x10c4: 0x24b1, 0x10c5: 0x24db, + 0x10c6: 0x24db, 0x10c7: 0x24e2, 0x10c8: 0x24e2, 0x10c9: 0x250c, 0x10ca: 0x2513, 0x10cb: 0x251a, + 0x10cc: 0x24f0, 0x10cd: 0x24fe, 0x10ce: 0x2521, 0x10cf: 0x2528, + 0x10d2: 0x24f7, 0x10d3: 0x257c, 0x10d4: 0x2583, 0x10d5: 0x2559, 0x10d6: 0x2560, 0x10d7: 0x2544, + 0x10d8: 0x2544, 0x10d9: 0x254b, 0x10da: 0x2575, 0x10db: 0x256e, 0x10dc: 0x2598, 0x10dd: 0x2598, + 0x10de: 0x2306, 0x10df: 0x231b, 0x10e0: 0x2314, 0x10e1: 0x233e, 0x10e2: 0x2337, 0x10e3: 0x2361, + 0x10e4: 0x235a, 0x10e5: 0x2384, 0x10e6: 0x2368, 0x10e7: 0x237d, 0x10e8: 0x23b5, 0x10e9: 0x2402, + 0x10ea: 0x23e6, 0x10eb: 0x2425, 0x10ec: 0x24bf, 0x10ed: 0x24e9, 0x10ee: 0x2591, 0x10ef: 0x258a, + 0x10f0: 0x259f, 0x10f1: 0x2536, 0x10f2: 0x249c, 0x10f3: 0x2567, 0x10f4: 0x248e, 0x10f5: 0x24c6, + 0x10f6: 0x245d, 0x10f7: 0x24aa, 0x10f8: 0x253d, 0x10f9: 0x252f, 0x10fa: 0x24b8, 0x10fb: 0x24a3, + 0x10fc: 0x24b8, 0x10fd: 0x253d, 0x10fe: 0x236f, 0x10ff: 0x238b, + // Block 0x44, offset 0x1100 + 0x1100: 0x2505, 0x1101: 0x2480, 0x1102: 0x22ff, 0x1103: 0x24a3, 0x1104: 0x2448, 0x1105: 0x2417, + 0x1106: 0x23bc, 0x1107: 0x2552, + 0x1130: 0x2410, 0x1131: 0x2487, 0x1132: 0x27bb, 0x1133: 0x27b2, 0x1134: 0x27e8, 0x1135: 0x27d6, + 0x1136: 0x27c4, 0x1137: 0x27df, 0x1138: 0x27f1, 0x1139: 0x2409, 0x113a: 0x2c78, 0x113b: 0x2af8, + 0x113c: 0x27cd, + // Block 0x45, offset 0x1140 + 0x1150: 0x0019, 0x1151: 0x0483, + 0x1152: 0x0487, 0x1153: 0x0035, 0x1154: 0x0037, 0x1155: 0x0003, 0x1156: 0x003f, 0x1157: 0x04bf, + 0x1158: 0x04c3, 0x1159: 0x1b58, + 0x1160: 0x8132, 0x1161: 0x8132, 0x1162: 0x8132, 0x1163: 0x8132, + 0x1164: 0x8132, 0x1165: 0x8132, 0x1166: 0x8132, 0x1167: 0x812d, 0x1168: 0x812d, 0x1169: 0x812d, + 0x116a: 0x812d, 0x116b: 0x812d, 0x116c: 0x812d, 0x116d: 0x812d, 0x116e: 0x8132, 0x116f: 0x8132, + 0x1170: 0x186f, 0x1171: 0x0443, 0x1172: 0x043f, 0x1173: 0x007f, 0x1174: 0x007f, 0x1175: 0x0011, + 0x1176: 0x0013, 0x1177: 0x00b7, 0x1178: 0x00bb, 0x1179: 0x04b7, 0x117a: 0x04bb, 0x117b: 0x04ab, + 0x117c: 0x04af, 0x117d: 0x0493, 0x117e: 0x0497, 0x117f: 0x048b, + // Block 0x46, offset 0x1180 + 0x1180: 0x048f, 0x1181: 0x049b, 0x1182: 0x049f, 0x1183: 0x04a3, 0x1184: 0x04a7, + 0x1187: 0x0077, 0x1188: 0x007b, 0x1189: 0x4265, 0x118a: 0x4265, 0x118b: 0x4265, + 0x118c: 0x4265, 0x118d: 0x007f, 0x118e: 0x007f, 0x118f: 0x007f, 0x1190: 0x0019, 0x1191: 0x0483, + 0x1192: 0x001d, 0x1194: 0x0037, 0x1195: 0x0035, 0x1196: 0x003f, 0x1197: 0x0003, + 0x1198: 0x0443, 0x1199: 0x0011, 0x119a: 0x0013, 0x119b: 0x00b7, 0x119c: 0x00bb, 0x119d: 0x04b7, + 0x119e: 0x04bb, 0x119f: 0x0007, 0x11a0: 0x000d, 0x11a1: 0x0015, 0x11a2: 0x0017, 0x11a3: 0x001b, + 0x11a4: 0x0039, 0x11a5: 0x003d, 0x11a6: 0x003b, 0x11a8: 0x0079, 0x11a9: 0x0009, + 0x11aa: 0x000b, 0x11ab: 0x0041, + 0x11b0: 0x42a6, 0x11b1: 0x44be, 0x11b2: 0x42ab, 0x11b4: 0x42b0, + 0x11b6: 0x42b5, 0x11b7: 0x44c4, 0x11b8: 0x42ba, 0x11b9: 0x44ca, 0x11ba: 0x42bf, 0x11bb: 0x44d0, + 0x11bc: 0x42c4, 0x11bd: 0x44d6, 0x11be: 0x42c9, 0x11bf: 0x44dc, + // Block 0x47, offset 0x11c0 + 0x11c0: 0x0236, 0x11c1: 0x44a0, 0x11c2: 0x44a0, 0x11c3: 0x44a6, 0x11c4: 0x44a6, 0x11c5: 0x44e8, + 0x11c6: 0x44e8, 0x11c7: 0x44ac, 0x11c8: 0x44ac, 0x11c9: 0x44f4, 0x11ca: 0x44f4, 0x11cb: 0x44f4, + 0x11cc: 0x44f4, 0x11cd: 0x0239, 0x11ce: 0x0239, 0x11cf: 0x023c, 0x11d0: 0x023c, 0x11d1: 0x023c, + 0x11d2: 0x023c, 0x11d3: 0x023f, 0x11d4: 0x023f, 0x11d5: 0x0242, 0x11d6: 0x0242, 0x11d7: 0x0242, + 0x11d8: 0x0242, 0x11d9: 0x0245, 0x11da: 0x0245, 0x11db: 0x0245, 0x11dc: 0x0245, 0x11dd: 0x0248, + 0x11de: 0x0248, 0x11df: 0x0248, 0x11e0: 0x0248, 0x11e1: 0x024b, 0x11e2: 0x024b, 0x11e3: 0x024b, + 0x11e4: 0x024b, 0x11e5: 0x024e, 0x11e6: 0x024e, 0x11e7: 0x024e, 0x11e8: 0x024e, 0x11e9: 0x0251, + 0x11ea: 0x0251, 0x11eb: 0x0254, 0x11ec: 0x0254, 0x11ed: 0x0257, 0x11ee: 0x0257, 0x11ef: 0x025a, + 0x11f0: 0x025a, 0x11f1: 0x025d, 0x11f2: 0x025d, 0x11f3: 0x025d, 0x11f4: 0x025d, 0x11f5: 0x0260, + 0x11f6: 0x0260, 0x11f7: 0x0260, 0x11f8: 0x0260, 0x11f9: 0x0263, 0x11fa: 0x0263, 0x11fb: 0x0263, + 0x11fc: 0x0263, 0x11fd: 0x0266, 0x11fe: 0x0266, 0x11ff: 0x0266, + // Block 0x48, offset 0x1200 + 0x1200: 0x0266, 0x1201: 0x0269, 0x1202: 0x0269, 0x1203: 0x0269, 0x1204: 0x0269, 0x1205: 0x026c, + 0x1206: 0x026c, 0x1207: 0x026c, 0x1208: 0x026c, 0x1209: 0x026f, 0x120a: 0x026f, 0x120b: 0x026f, + 0x120c: 0x026f, 0x120d: 0x0272, 0x120e: 0x0272, 0x120f: 0x0272, 0x1210: 0x0272, 0x1211: 0x0275, + 0x1212: 0x0275, 0x1213: 0x0275, 0x1214: 0x0275, 0x1215: 0x0278, 0x1216: 0x0278, 0x1217: 0x0278, + 0x1218: 0x0278, 0x1219: 0x027b, 0x121a: 0x027b, 0x121b: 0x027b, 0x121c: 0x027b, 0x121d: 0x027e, + 0x121e: 0x027e, 0x121f: 0x027e, 0x1220: 0x027e, 0x1221: 0x0281, 0x1222: 0x0281, 0x1223: 0x0281, + 0x1224: 0x0281, 0x1225: 0x0284, 0x1226: 0x0284, 0x1227: 0x0284, 0x1228: 0x0284, 0x1229: 0x0287, + 0x122a: 0x0287, 0x122b: 0x0287, 0x122c: 0x0287, 0x122d: 0x028a, 0x122e: 0x028a, 0x122f: 0x028d, + 0x1230: 0x028d, 0x1231: 0x0290, 0x1232: 0x0290, 0x1233: 0x0290, 0x1234: 0x0290, 0x1235: 0x2dfc, + 0x1236: 0x2dfc, 0x1237: 0x2e04, 0x1238: 0x2e04, 0x1239: 0x2e0c, 0x123a: 0x2e0c, 0x123b: 0x1f7e, + 0x123c: 0x1f7e, + // Block 0x49, offset 0x1240 + 0x1240: 0x0081, 0x1241: 0x0083, 0x1242: 0x0085, 0x1243: 0x0087, 0x1244: 0x0089, 0x1245: 0x008b, + 0x1246: 0x008d, 0x1247: 0x008f, 0x1248: 0x0091, 0x1249: 0x0093, 0x124a: 0x0095, 0x124b: 0x0097, + 0x124c: 0x0099, 0x124d: 0x009b, 0x124e: 0x009d, 0x124f: 0x009f, 0x1250: 0x00a1, 0x1251: 0x00a3, + 0x1252: 0x00a5, 0x1253: 0x00a7, 0x1254: 0x00a9, 0x1255: 0x00ab, 0x1256: 0x00ad, 0x1257: 0x00af, + 0x1258: 0x00b1, 0x1259: 0x00b3, 0x125a: 0x00b5, 0x125b: 0x00b7, 0x125c: 0x00b9, 0x125d: 0x00bb, + 0x125e: 0x00bd, 0x125f: 0x0477, 0x1260: 0x047b, 0x1261: 0x0487, 0x1262: 0x049b, 0x1263: 0x049f, + 0x1264: 0x0483, 0x1265: 0x05ab, 0x1266: 0x05a3, 0x1267: 0x04c7, 0x1268: 0x04cf, 0x1269: 0x04d7, + 0x126a: 0x04df, 0x126b: 0x04e7, 0x126c: 0x056b, 0x126d: 0x0573, 0x126e: 0x057b, 0x126f: 0x051f, + 0x1270: 0x05af, 0x1271: 0x04cb, 0x1272: 0x04d3, 0x1273: 0x04db, 0x1274: 0x04e3, 0x1275: 0x04eb, + 0x1276: 0x04ef, 0x1277: 0x04f3, 0x1278: 0x04f7, 0x1279: 0x04fb, 0x127a: 0x04ff, 0x127b: 0x0503, + 0x127c: 0x0507, 0x127d: 0x050b, 0x127e: 0x050f, 0x127f: 0x0513, + // Block 0x4a, offset 0x1280 + 0x1280: 0x0517, 0x1281: 0x051b, 0x1282: 0x0523, 0x1283: 0x0527, 0x1284: 0x052b, 0x1285: 0x052f, + 0x1286: 0x0533, 0x1287: 0x0537, 0x1288: 0x053b, 0x1289: 0x053f, 0x128a: 0x0543, 0x128b: 0x0547, + 0x128c: 0x054b, 0x128d: 0x054f, 0x128e: 0x0553, 0x128f: 0x0557, 0x1290: 0x055b, 0x1291: 0x055f, + 0x1292: 0x0563, 0x1293: 0x0567, 0x1294: 0x056f, 0x1295: 0x0577, 0x1296: 0x057f, 0x1297: 0x0583, + 0x1298: 0x0587, 0x1299: 0x058b, 0x129a: 0x058f, 0x129b: 0x0593, 0x129c: 0x0597, 0x129d: 0x05a7, + 0x129e: 0x4a56, 0x129f: 0x4a5c, 0x12a0: 0x03c3, 0x12a1: 0x0313, 0x12a2: 0x0317, 0x12a3: 0x4341, + 0x12a4: 0x031b, 0x12a5: 0x4346, 0x12a6: 0x434b, 0x12a7: 0x031f, 0x12a8: 0x0323, 0x12a9: 0x0327, + 0x12aa: 0x4350, 0x12ab: 0x4355, 0x12ac: 0x435a, 0x12ad: 0x435f, 0x12ae: 0x4364, 0x12af: 0x4369, + 0x12b0: 0x0367, 0x12b1: 0x032b, 0x12b2: 0x032f, 0x12b3: 0x0333, 0x12b4: 0x037b, 0x12b5: 0x0337, + 0x12b6: 0x033b, 0x12b7: 0x033f, 0x12b8: 0x0343, 0x12b9: 0x0347, 0x12ba: 0x034b, 0x12bb: 0x034f, + 0x12bc: 0x0353, 0x12bd: 0x0357, 0x12be: 0x035b, + // Block 0x4b, offset 0x12c0 + 0x12c2: 0x42d8, 0x12c3: 0x42dd, 0x12c4: 0x42e2, 0x12c5: 0x42e7, + 0x12c6: 0x42ec, 0x12c7: 0x42f1, 0x12ca: 0x42f6, 0x12cb: 0x42fb, + 0x12cc: 0x4300, 0x12cd: 0x4305, 0x12ce: 0x430a, 0x12cf: 0x430f, + 0x12d2: 0x4314, 0x12d3: 0x4319, 0x12d4: 0x431e, 0x12d5: 0x4323, 0x12d6: 0x4328, 0x12d7: 0x432d, + 0x12da: 0x4332, 0x12db: 0x4337, 0x12dc: 0x433c, + 0x12e0: 0x00bf, 0x12e1: 0x00c2, 0x12e2: 0x00cb, 0x12e3: 0x4260, + 0x12e4: 0x00c8, 0x12e5: 0x00c5, 0x12e6: 0x0447, 0x12e8: 0x046b, 0x12e9: 0x044b, + 0x12ea: 0x044f, 0x12eb: 0x0453, 0x12ec: 0x0457, 0x12ed: 0x046f, 0x12ee: 0x0473, + // Block 0x4c, offset 0x1300 + 0x1300: 0x0063, 0x1301: 0x0065, 0x1302: 0x0067, 0x1303: 0x0069, 0x1304: 0x006b, 0x1305: 0x006d, + 0x1306: 0x006f, 0x1307: 0x0071, 0x1308: 0x0073, 0x1309: 0x0075, 0x130a: 0x0083, 0x130b: 0x0085, + 0x130c: 0x0087, 0x130d: 0x0089, 0x130e: 0x008b, 0x130f: 0x008d, 0x1310: 0x008f, 0x1311: 0x0091, + 0x1312: 0x0093, 0x1313: 0x0095, 0x1314: 0x0097, 0x1315: 0x0099, 0x1316: 0x009b, 0x1317: 0x009d, + 0x1318: 0x009f, 0x1319: 0x00a1, 0x131a: 0x00a3, 0x131b: 0x00a5, 0x131c: 0x00a7, 0x131d: 0x00a9, + 0x131e: 0x00ab, 0x131f: 0x00ad, 0x1320: 0x00af, 0x1321: 0x00b1, 0x1322: 0x00b3, 0x1323: 0x00b5, + 0x1324: 0x00dd, 0x1325: 0x00f2, 0x1328: 0x0173, 0x1329: 0x0176, + 0x132a: 0x0179, 0x132b: 0x017c, 0x132c: 0x017f, 0x132d: 0x0182, 0x132e: 0x0185, 0x132f: 0x0188, + 0x1330: 0x018b, 0x1331: 0x018e, 0x1332: 0x0191, 0x1333: 0x0194, 0x1334: 0x0197, 0x1335: 0x019a, + 0x1336: 0x019d, 0x1337: 0x01a0, 0x1338: 0x01a3, 0x1339: 0x0188, 0x133a: 0x01a6, 0x133b: 0x01a9, + 0x133c: 0x01ac, 0x133d: 0x01af, 0x133e: 0x01b2, 0x133f: 0x01b5, + // Block 0x4d, offset 0x1340 + 0x1340: 0x01fd, 0x1341: 0x0200, 0x1342: 0x0203, 0x1343: 0x045b, 0x1344: 0x01c7, 0x1345: 0x01d0, + 0x1346: 0x01d6, 0x1347: 0x01fa, 0x1348: 0x01eb, 0x1349: 0x01e8, 0x134a: 0x0206, 0x134b: 0x0209, + 0x134e: 0x0021, 0x134f: 0x0023, 0x1350: 0x0025, 0x1351: 0x0027, + 0x1352: 0x0029, 0x1353: 0x002b, 0x1354: 0x002d, 0x1355: 0x002f, 0x1356: 0x0031, 0x1357: 0x0033, + 0x1358: 0x0021, 0x1359: 0x0023, 0x135a: 0x0025, 0x135b: 0x0027, 0x135c: 0x0029, 0x135d: 0x002b, + 0x135e: 0x002d, 0x135f: 0x002f, 0x1360: 0x0031, 0x1361: 0x0033, 0x1362: 0x0021, 0x1363: 0x0023, + 0x1364: 0x0025, 0x1365: 0x0027, 0x1366: 0x0029, 0x1367: 0x002b, 0x1368: 0x002d, 0x1369: 0x002f, + 0x136a: 0x0031, 0x136b: 0x0033, 0x136c: 0x0021, 0x136d: 0x0023, 0x136e: 0x0025, 0x136f: 0x0027, + 0x1370: 0x0029, 0x1371: 0x002b, 0x1372: 0x002d, 0x1373: 0x002f, 0x1374: 0x0031, 0x1375: 0x0033, + 0x1376: 0x0021, 0x1377: 0x0023, 0x1378: 0x0025, 0x1379: 0x0027, 0x137a: 0x0029, 0x137b: 0x002b, + 0x137c: 0x002d, 0x137d: 0x002f, 0x137e: 0x0031, 0x137f: 0x0033, + // Block 0x4e, offset 0x1380 + 0x1380: 0x0239, 0x1381: 0x023c, 0x1382: 0x0248, 0x1383: 0x0251, 0x1385: 0x028a, + 0x1386: 0x025a, 0x1387: 0x024b, 0x1388: 0x0269, 0x1389: 0x0290, 0x138a: 0x027b, 0x138b: 0x027e, + 0x138c: 0x0281, 0x138d: 0x0284, 0x138e: 0x025d, 0x138f: 0x026f, 0x1390: 0x0275, 0x1391: 0x0263, + 0x1392: 0x0278, 0x1393: 0x0257, 0x1394: 0x0260, 0x1395: 0x0242, 0x1396: 0x0245, 0x1397: 0x024e, + 0x1398: 0x0254, 0x1399: 0x0266, 0x139a: 0x026c, 0x139b: 0x0272, 0x139c: 0x0293, 0x139d: 0x02e4, + 0x139e: 0x02cc, 0x139f: 0x0296, 0x13a1: 0x023c, 0x13a2: 0x0248, + 0x13a4: 0x0287, 0x13a7: 0x024b, 0x13a9: 0x0290, + 0x13aa: 0x027b, 0x13ab: 0x027e, 0x13ac: 0x0281, 0x13ad: 0x0284, 0x13ae: 0x025d, 0x13af: 0x026f, + 0x13b0: 0x0275, 0x13b1: 0x0263, 0x13b2: 0x0278, 0x13b4: 0x0260, 0x13b5: 0x0242, + 0x13b6: 0x0245, 0x13b7: 0x024e, 0x13b9: 0x0266, 0x13bb: 0x0272, + // Block 0x4f, offset 0x13c0 + 0x13c2: 0x0248, + 0x13c7: 0x024b, 0x13c9: 0x0290, 0x13cb: 0x027e, + 0x13cd: 0x0284, 0x13ce: 0x025d, 0x13cf: 0x026f, 0x13d1: 0x0263, + 0x13d2: 0x0278, 0x13d4: 0x0260, 0x13d7: 0x024e, + 0x13d9: 0x0266, 0x13db: 0x0272, 0x13dd: 0x02e4, + 0x13df: 0x0296, 0x13e1: 0x023c, 0x13e2: 0x0248, + 0x13e4: 0x0287, 0x13e7: 0x024b, 0x13e8: 0x0269, 0x13e9: 0x0290, + 0x13ea: 0x027b, 0x13ec: 0x0281, 0x13ed: 0x0284, 0x13ee: 0x025d, 0x13ef: 0x026f, + 0x13f0: 0x0275, 0x13f1: 0x0263, 0x13f2: 0x0278, 0x13f4: 0x0260, 0x13f5: 0x0242, + 0x13f6: 0x0245, 0x13f7: 0x024e, 0x13f9: 0x0266, 0x13fa: 0x026c, 0x13fb: 0x0272, + 0x13fc: 0x0293, 0x13fe: 0x02cc, + // Block 0x50, offset 0x1400 + 0x1400: 0x0239, 0x1401: 0x023c, 0x1402: 0x0248, 0x1403: 0x0251, 0x1404: 0x0287, 0x1405: 0x028a, + 0x1406: 0x025a, 0x1407: 0x024b, 0x1408: 0x0269, 0x1409: 0x0290, 0x140b: 0x027e, + 0x140c: 0x0281, 0x140d: 0x0284, 0x140e: 0x025d, 0x140f: 0x026f, 0x1410: 0x0275, 0x1411: 0x0263, + 0x1412: 0x0278, 0x1413: 0x0257, 0x1414: 0x0260, 0x1415: 0x0242, 0x1416: 0x0245, 0x1417: 0x024e, + 0x1418: 0x0254, 0x1419: 0x0266, 0x141a: 0x026c, 0x141b: 0x0272, + 0x1421: 0x023c, 0x1422: 0x0248, 0x1423: 0x0251, + 0x1425: 0x028a, 0x1426: 0x025a, 0x1427: 0x024b, 0x1428: 0x0269, 0x1429: 0x0290, + 0x142b: 0x027e, 0x142c: 0x0281, 0x142d: 0x0284, 0x142e: 0x025d, 0x142f: 0x026f, + 0x1430: 0x0275, 0x1431: 0x0263, 0x1432: 0x0278, 0x1433: 0x0257, 0x1434: 0x0260, 0x1435: 0x0242, + 0x1436: 0x0245, 0x1437: 0x024e, 0x1438: 0x0254, 0x1439: 0x0266, 0x143a: 0x026c, 0x143b: 0x0272, + // Block 0x51, offset 0x1440 + 0x1440: 0x1875, 0x1441: 0x1872, 0x1442: 0x1878, 0x1443: 0x189c, 0x1444: 0x18c0, 0x1445: 0x18e4, + 0x1446: 0x1908, 0x1447: 0x1911, 0x1448: 0x1917, 0x1449: 0x191d, 0x144a: 0x1923, + 0x1450: 0x1a88, 0x1451: 0x1a8c, + 0x1452: 0x1a90, 0x1453: 0x1a94, 0x1454: 0x1a98, 0x1455: 0x1a9c, 0x1456: 0x1aa0, 0x1457: 0x1aa4, + 0x1458: 0x1aa8, 0x1459: 0x1aac, 0x145a: 0x1ab0, 0x145b: 0x1ab4, 0x145c: 0x1ab8, 0x145d: 0x1abc, + 0x145e: 0x1ac0, 0x145f: 0x1ac4, 0x1460: 0x1ac8, 0x1461: 0x1acc, 0x1462: 0x1ad0, 0x1463: 0x1ad4, + 0x1464: 0x1ad8, 0x1465: 0x1adc, 0x1466: 0x1ae0, 0x1467: 0x1ae4, 0x1468: 0x1ae8, 0x1469: 0x1aec, + 0x146a: 0x271a, 0x146b: 0x0047, 0x146c: 0x0065, 0x146d: 0x1938, 0x146e: 0x19ad, + 0x1470: 0x0043, 0x1471: 0x0045, 0x1472: 0x0047, 0x1473: 0x0049, 0x1474: 0x004b, 0x1475: 0x004d, + 0x1476: 0x004f, 0x1477: 0x0051, 0x1478: 0x0053, 0x1479: 0x0055, 0x147a: 0x0057, 0x147b: 0x0059, + 0x147c: 0x005b, 0x147d: 0x005d, 0x147e: 0x005f, 0x147f: 0x0061, + // Block 0x52, offset 0x1480 + 0x1480: 0x26a9, 0x1481: 0x26be, 0x1482: 0x0503, + 0x1490: 0x0c0f, 0x1491: 0x0a47, + 0x1492: 0x08d3, 0x1493: 0x4656, 0x1494: 0x071b, 0x1495: 0x09ef, 0x1496: 0x132f, 0x1497: 0x09ff, + 0x1498: 0x0727, 0x1499: 0x0cd7, 0x149a: 0x0eaf, 0x149b: 0x0caf, 0x149c: 0x0827, 0x149d: 0x0b6b, + 0x149e: 0x07bf, 0x149f: 0x0cb7, 0x14a0: 0x0813, 0x14a1: 0x1117, 0x14a2: 0x0f83, 0x14a3: 0x138b, + 0x14a4: 0x09d3, 0x14a5: 0x090b, 0x14a6: 0x0e63, 0x14a7: 0x0c1b, 0x14a8: 0x0c47, 0x14a9: 0x06bf, + 0x14aa: 0x06cb, 0x14ab: 0x140b, 0x14ac: 0x0adb, 0x14ad: 0x06e7, 0x14ae: 0x08ef, 0x14af: 0x0c3b, + 0x14b0: 0x13b3, 0x14b1: 0x0c13, 0x14b2: 0x106f, 0x14b3: 0x10ab, 0x14b4: 0x08f7, 0x14b5: 0x0e43, + 0x14b6: 0x0d0b, 0x14b7: 0x0d07, 0x14b8: 0x0f97, 0x14b9: 0x082b, 0x14ba: 0x0957, + // Block 0x53, offset 0x14c0 + 0x14c0: 0x06fb, 0x14c1: 0x06f3, 0x14c2: 0x0703, 0x14c3: 0x1643, 0x14c4: 0x0747, 0x14c5: 0x0757, + 0x14c6: 0x075b, 0x14c7: 0x0763, 0x14c8: 0x076b, 0x14c9: 0x076f, 0x14ca: 0x077b, 0x14cb: 0x0773, + 0x14cc: 0x05b3, 0x14cd: 0x1657, 0x14ce: 0x078f, 0x14cf: 0x0793, 0x14d0: 0x0797, 0x14d1: 0x07b3, + 0x14d2: 0x1648, 0x14d3: 0x05b7, 0x14d4: 0x079f, 0x14d5: 0x07bf, 0x14d6: 0x1652, 0x14d7: 0x07cf, + 0x14d8: 0x07d7, 0x14d9: 0x0737, 0x14da: 0x07df, 0x14db: 0x07e3, 0x14dc: 0x182d, 0x14dd: 0x07ff, + 0x14de: 0x0807, 0x14df: 0x05bf, 0x14e0: 0x081f, 0x14e1: 0x0823, 0x14e2: 0x082b, 0x14e3: 0x082f, + 0x14e4: 0x05c3, 0x14e5: 0x0847, 0x14e6: 0x084b, 0x14e7: 0x0857, 0x14e8: 0x0863, 0x14e9: 0x0867, + 0x14ea: 0x086b, 0x14eb: 0x0873, 0x14ec: 0x0893, 0x14ed: 0x0897, 0x14ee: 0x089f, 0x14ef: 0x08af, + 0x14f0: 0x08b7, 0x14f1: 0x08bb, 0x14f2: 0x08bb, 0x14f3: 0x08bb, 0x14f4: 0x1666, 0x14f5: 0x0e93, + 0x14f6: 0x08cf, 0x14f7: 0x08d7, 0x14f8: 0x166b, 0x14f9: 0x08e3, 0x14fa: 0x08eb, 0x14fb: 0x08f3, + 0x14fc: 0x091b, 0x14fd: 0x0907, 0x14fe: 0x0913, 0x14ff: 0x0917, + // Block 0x54, offset 0x1500 + 0x1500: 0x091f, 0x1501: 0x0927, 0x1502: 0x092b, 0x1503: 0x0933, 0x1504: 0x093b, 0x1505: 0x093f, + 0x1506: 0x093f, 0x1507: 0x0947, 0x1508: 0x094f, 0x1509: 0x0953, 0x150a: 0x095f, 0x150b: 0x0983, + 0x150c: 0x0967, 0x150d: 0x0987, 0x150e: 0x096b, 0x150f: 0x0973, 0x1510: 0x080b, 0x1511: 0x09cf, + 0x1512: 0x0997, 0x1513: 0x099b, 0x1514: 0x099f, 0x1515: 0x0993, 0x1516: 0x09a7, 0x1517: 0x09a3, + 0x1518: 0x09bb, 0x1519: 0x1670, 0x151a: 0x09d7, 0x151b: 0x09db, 0x151c: 0x09e3, 0x151d: 0x09ef, + 0x151e: 0x09f7, 0x151f: 0x0a13, 0x1520: 0x1675, 0x1521: 0x167a, 0x1522: 0x0a1f, 0x1523: 0x0a23, + 0x1524: 0x0a27, 0x1525: 0x0a1b, 0x1526: 0x0a2f, 0x1527: 0x05c7, 0x1528: 0x05cb, 0x1529: 0x0a37, + 0x152a: 0x0a3f, 0x152b: 0x0a3f, 0x152c: 0x167f, 0x152d: 0x0a5b, 0x152e: 0x0a5f, 0x152f: 0x0a63, + 0x1530: 0x0a6b, 0x1531: 0x1684, 0x1532: 0x0a73, 0x1533: 0x0a77, 0x1534: 0x0b4f, 0x1535: 0x0a7f, + 0x1536: 0x05cf, 0x1537: 0x0a8b, 0x1538: 0x0a9b, 0x1539: 0x0aa7, 0x153a: 0x0aa3, 0x153b: 0x168e, + 0x153c: 0x0aaf, 0x153d: 0x1693, 0x153e: 0x0abb, 0x153f: 0x0ab7, + // Block 0x55, offset 0x1540 + 0x1540: 0x0abf, 0x1541: 0x0acf, 0x1542: 0x0ad3, 0x1543: 0x05d3, 0x1544: 0x0ae3, 0x1545: 0x0aeb, + 0x1546: 0x0aef, 0x1547: 0x0af3, 0x1548: 0x05d7, 0x1549: 0x1698, 0x154a: 0x05db, 0x154b: 0x0b0f, + 0x154c: 0x0b13, 0x154d: 0x0b17, 0x154e: 0x0b1f, 0x154f: 0x185f, 0x1550: 0x0b37, 0x1551: 0x16a2, + 0x1552: 0x16a2, 0x1553: 0x11d7, 0x1554: 0x0b47, 0x1555: 0x0b47, 0x1556: 0x05df, 0x1557: 0x16c5, + 0x1558: 0x1797, 0x1559: 0x0b57, 0x155a: 0x0b5f, 0x155b: 0x05e3, 0x155c: 0x0b73, 0x155d: 0x0b83, + 0x155e: 0x0b87, 0x155f: 0x0b8f, 0x1560: 0x0b9f, 0x1561: 0x05eb, 0x1562: 0x05e7, 0x1563: 0x0ba3, + 0x1564: 0x16a7, 0x1565: 0x0ba7, 0x1566: 0x0bbb, 0x1567: 0x0bbf, 0x1568: 0x0bc3, 0x1569: 0x0bbf, + 0x156a: 0x0bcf, 0x156b: 0x0bd3, 0x156c: 0x0be3, 0x156d: 0x0bdb, 0x156e: 0x0bdf, 0x156f: 0x0be7, + 0x1570: 0x0beb, 0x1571: 0x0bef, 0x1572: 0x0bfb, 0x1573: 0x0bff, 0x1574: 0x0c17, 0x1575: 0x0c1f, + 0x1576: 0x0c2f, 0x1577: 0x0c43, 0x1578: 0x16b6, 0x1579: 0x0c3f, 0x157a: 0x0c33, 0x157b: 0x0c4b, + 0x157c: 0x0c53, 0x157d: 0x0c67, 0x157e: 0x16bb, 0x157f: 0x0c6f, + // Block 0x56, offset 0x1580 + 0x1580: 0x0c63, 0x1581: 0x0c5b, 0x1582: 0x05ef, 0x1583: 0x0c77, 0x1584: 0x0c7f, 0x1585: 0x0c87, + 0x1586: 0x0c7b, 0x1587: 0x05f3, 0x1588: 0x0c97, 0x1589: 0x0c9f, 0x158a: 0x16c0, 0x158b: 0x0ccb, + 0x158c: 0x0cff, 0x158d: 0x0cdb, 0x158e: 0x05ff, 0x158f: 0x0ce7, 0x1590: 0x05fb, 0x1591: 0x05f7, + 0x1592: 0x07c3, 0x1593: 0x07c7, 0x1594: 0x0d03, 0x1595: 0x0ceb, 0x1596: 0x11ab, 0x1597: 0x0663, + 0x1598: 0x0d0f, 0x1599: 0x0d13, 0x159a: 0x0d17, 0x159b: 0x0d2b, 0x159c: 0x0d23, 0x159d: 0x16d9, + 0x159e: 0x0603, 0x159f: 0x0d3f, 0x15a0: 0x0d33, 0x15a1: 0x0d4f, 0x15a2: 0x0d57, 0x15a3: 0x16e3, + 0x15a4: 0x0d5b, 0x15a5: 0x0d47, 0x15a6: 0x0d63, 0x15a7: 0x0607, 0x15a8: 0x0d67, 0x15a9: 0x0d6b, + 0x15aa: 0x0d6f, 0x15ab: 0x0d7b, 0x15ac: 0x16e8, 0x15ad: 0x0d83, 0x15ae: 0x060b, 0x15af: 0x0d8f, + 0x15b0: 0x16ed, 0x15b1: 0x0d93, 0x15b2: 0x060f, 0x15b3: 0x0d9f, 0x15b4: 0x0dab, 0x15b5: 0x0db7, + 0x15b6: 0x0dbb, 0x15b7: 0x16f2, 0x15b8: 0x1689, 0x15b9: 0x16f7, 0x15ba: 0x0ddb, 0x15bb: 0x16fc, + 0x15bc: 0x0de7, 0x15bd: 0x0def, 0x15be: 0x0ddf, 0x15bf: 0x0dfb, + // Block 0x57, offset 0x15c0 + 0x15c0: 0x0e0b, 0x15c1: 0x0e1b, 0x15c2: 0x0e0f, 0x15c3: 0x0e13, 0x15c4: 0x0e1f, 0x15c5: 0x0e23, + 0x15c6: 0x1701, 0x15c7: 0x0e07, 0x15c8: 0x0e3b, 0x15c9: 0x0e3f, 0x15ca: 0x0613, 0x15cb: 0x0e53, + 0x15cc: 0x0e4f, 0x15cd: 0x1706, 0x15ce: 0x0e33, 0x15cf: 0x0e6f, 0x15d0: 0x170b, 0x15d1: 0x1710, + 0x15d2: 0x0e73, 0x15d3: 0x0e87, 0x15d4: 0x0e83, 0x15d5: 0x0e7f, 0x15d6: 0x0617, 0x15d7: 0x0e8b, + 0x15d8: 0x0e9b, 0x15d9: 0x0e97, 0x15da: 0x0ea3, 0x15db: 0x164d, 0x15dc: 0x0eb3, 0x15dd: 0x1715, + 0x15de: 0x0ebf, 0x15df: 0x171f, 0x15e0: 0x0ed3, 0x15e1: 0x0edf, 0x15e2: 0x0ef3, 0x15e3: 0x1724, + 0x15e4: 0x0f07, 0x15e5: 0x0f0b, 0x15e6: 0x1729, 0x15e7: 0x172e, 0x15e8: 0x0f27, 0x15e9: 0x0f37, + 0x15ea: 0x061b, 0x15eb: 0x0f3b, 0x15ec: 0x061f, 0x15ed: 0x061f, 0x15ee: 0x0f53, 0x15ef: 0x0f57, + 0x15f0: 0x0f5f, 0x15f1: 0x0f63, 0x15f2: 0x0f6f, 0x15f3: 0x0623, 0x15f4: 0x0f87, 0x15f5: 0x1733, + 0x15f6: 0x0fa3, 0x15f7: 0x1738, 0x15f8: 0x0faf, 0x15f9: 0x169d, 0x15fa: 0x0fbf, 0x15fb: 0x173d, + 0x15fc: 0x1742, 0x15fd: 0x1747, 0x15fe: 0x0627, 0x15ff: 0x062b, + // Block 0x58, offset 0x1600 + 0x1600: 0x0ff7, 0x1601: 0x1751, 0x1602: 0x174c, 0x1603: 0x1756, 0x1604: 0x175b, 0x1605: 0x0fff, + 0x1606: 0x1003, 0x1607: 0x1003, 0x1608: 0x100b, 0x1609: 0x0633, 0x160a: 0x100f, 0x160b: 0x0637, + 0x160c: 0x063b, 0x160d: 0x1765, 0x160e: 0x1023, 0x160f: 0x102b, 0x1610: 0x1037, 0x1611: 0x063f, + 0x1612: 0x176a, 0x1613: 0x105b, 0x1614: 0x176f, 0x1615: 0x1774, 0x1616: 0x107b, 0x1617: 0x1093, + 0x1618: 0x0643, 0x1619: 0x109b, 0x161a: 0x109f, 0x161b: 0x10a3, 0x161c: 0x1779, 0x161d: 0x177e, + 0x161e: 0x177e, 0x161f: 0x10bb, 0x1620: 0x0647, 0x1621: 0x1783, 0x1622: 0x10cf, 0x1623: 0x10d3, + 0x1624: 0x064b, 0x1625: 0x1788, 0x1626: 0x10ef, 0x1627: 0x064f, 0x1628: 0x10ff, 0x1629: 0x10f7, + 0x162a: 0x1107, 0x162b: 0x1792, 0x162c: 0x111f, 0x162d: 0x0653, 0x162e: 0x112b, 0x162f: 0x1133, + 0x1630: 0x1143, 0x1631: 0x0657, 0x1632: 0x179c, 0x1633: 0x17a1, 0x1634: 0x065b, 0x1635: 0x17a6, + 0x1636: 0x115b, 0x1637: 0x17ab, 0x1638: 0x1167, 0x1639: 0x1173, 0x163a: 0x117b, 0x163b: 0x17b0, + 0x163c: 0x17b5, 0x163d: 0x118f, 0x163e: 0x17ba, 0x163f: 0x1197, + // Block 0x59, offset 0x1640 + 0x1640: 0x16ca, 0x1641: 0x065f, 0x1642: 0x11af, 0x1643: 0x11b3, 0x1644: 0x0667, 0x1645: 0x11b7, + 0x1646: 0x0a33, 0x1647: 0x17bf, 0x1648: 0x17c4, 0x1649: 0x16cf, 0x164a: 0x16d4, 0x164b: 0x11d7, + 0x164c: 0x11db, 0x164d: 0x13f3, 0x164e: 0x066b, 0x164f: 0x1207, 0x1650: 0x1203, 0x1651: 0x120b, + 0x1652: 0x083f, 0x1653: 0x120f, 0x1654: 0x1213, 0x1655: 0x1217, 0x1656: 0x121f, 0x1657: 0x17c9, + 0x1658: 0x121b, 0x1659: 0x1223, 0x165a: 0x1237, 0x165b: 0x123b, 0x165c: 0x1227, 0x165d: 0x123f, + 0x165e: 0x1253, 0x165f: 0x1267, 0x1660: 0x1233, 0x1661: 0x1247, 0x1662: 0x124b, 0x1663: 0x124f, + 0x1664: 0x17ce, 0x1665: 0x17d8, 0x1666: 0x17d3, 0x1667: 0x066f, 0x1668: 0x126f, 0x1669: 0x1273, + 0x166a: 0x127b, 0x166b: 0x17ec, 0x166c: 0x127f, 0x166d: 0x17dd, 0x166e: 0x0673, 0x166f: 0x0677, + 0x1670: 0x17e2, 0x1671: 0x17e7, 0x1672: 0x067b, 0x1673: 0x129f, 0x1674: 0x12a3, 0x1675: 0x12a7, + 0x1676: 0x12ab, 0x1677: 0x12b7, 0x1678: 0x12b3, 0x1679: 0x12bf, 0x167a: 0x12bb, 0x167b: 0x12cb, + 0x167c: 0x12c3, 0x167d: 0x12c7, 0x167e: 0x12cf, 0x167f: 0x067f, + // Block 0x5a, offset 0x1680 + 0x1680: 0x12d7, 0x1681: 0x12db, 0x1682: 0x0683, 0x1683: 0x12eb, 0x1684: 0x12ef, 0x1685: 0x17f1, + 0x1686: 0x12fb, 0x1687: 0x12ff, 0x1688: 0x0687, 0x1689: 0x130b, 0x168a: 0x05bb, 0x168b: 0x17f6, + 0x168c: 0x17fb, 0x168d: 0x068b, 0x168e: 0x068f, 0x168f: 0x1337, 0x1690: 0x134f, 0x1691: 0x136b, + 0x1692: 0x137b, 0x1693: 0x1800, 0x1694: 0x138f, 0x1695: 0x1393, 0x1696: 0x13ab, 0x1697: 0x13b7, + 0x1698: 0x180a, 0x1699: 0x165c, 0x169a: 0x13c3, 0x169b: 0x13bf, 0x169c: 0x13cb, 0x169d: 0x1661, + 0x169e: 0x13d7, 0x169f: 0x13e3, 0x16a0: 0x180f, 0x16a1: 0x1814, 0x16a2: 0x1423, 0x16a3: 0x142f, + 0x16a4: 0x1437, 0x16a5: 0x1819, 0x16a6: 0x143b, 0x16a7: 0x1463, 0x16a8: 0x146f, 0x16a9: 0x1473, + 0x16aa: 0x146b, 0x16ab: 0x147f, 0x16ac: 0x1483, 0x16ad: 0x181e, 0x16ae: 0x148f, 0x16af: 0x0693, + 0x16b0: 0x1497, 0x16b1: 0x1823, 0x16b2: 0x0697, 0x16b3: 0x14cf, 0x16b4: 0x0ac3, 0x16b5: 0x14e7, + 0x16b6: 0x1828, 0x16b7: 0x1832, 0x16b8: 0x069b, 0x16b9: 0x069f, 0x16ba: 0x150f, 0x16bb: 0x1837, + 0x16bc: 0x06a3, 0x16bd: 0x183c, 0x16be: 0x1527, 0x16bf: 0x1527, + // Block 0x5b, offset 0x16c0 + 0x16c0: 0x152f, 0x16c1: 0x1841, 0x16c2: 0x1547, 0x16c3: 0x06a7, 0x16c4: 0x1557, 0x16c5: 0x1563, + 0x16c6: 0x156b, 0x16c7: 0x1573, 0x16c8: 0x06ab, 0x16c9: 0x1846, 0x16ca: 0x1587, 0x16cb: 0x15a3, + 0x16cc: 0x15af, 0x16cd: 0x06af, 0x16ce: 0x06b3, 0x16cf: 0x15b3, 0x16d0: 0x184b, 0x16d1: 0x06b7, + 0x16d2: 0x1850, 0x16d3: 0x1855, 0x16d4: 0x185a, 0x16d5: 0x15d7, 0x16d6: 0x06bb, 0x16d7: 0x15eb, + 0x16d8: 0x15f3, 0x16d9: 0x15f7, 0x16da: 0x15ff, 0x16db: 0x1607, 0x16dc: 0x160f, 0x16dd: 0x1864, +} + +// nfkcIndex: 22 blocks, 1408 entries, 1408 bytes +// Block 0 is the zero block. +var nfkcIndex = [1408]uint8{ + // Block 0x0, offset 0x0 + // Block 0x1, offset 0x40 + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0xc2: 0x5a, 0xc3: 0x01, 0xc4: 0x02, 0xc5: 0x03, 0xc6: 0x5b, 0xc7: 0x04, + 0xc8: 0x05, 0xca: 0x5c, 0xcb: 0x5d, 0xcc: 0x06, 0xcd: 0x07, 0xce: 0x08, 0xcf: 0x09, + 0xd0: 0x0a, 0xd1: 0x5e, 0xd2: 0x5f, 0xd3: 0x0b, 0xd6: 0x0c, 0xd7: 0x60, + 0xd8: 0x61, 0xd9: 0x0d, 0xdb: 0x62, 0xdc: 0x63, 0xdd: 0x64, 0xdf: 0x65, + 0xe0: 0x02, 0xe1: 0x03, 0xe2: 0x04, 0xe3: 0x05, + 0xea: 0x06, 0xeb: 0x07, 0xec: 0x08, 0xed: 0x09, 0xef: 0x0a, + 0xf0: 0x13, + // Block 0x4, offset 0x100 + 0x120: 0x66, 0x121: 0x67, 0x123: 0x68, 0x124: 0x69, 0x125: 0x6a, 0x126: 0x6b, 0x127: 0x6c, + 0x128: 0x6d, 0x129: 0x6e, 0x12a: 0x6f, 0x12b: 0x70, 0x12c: 0x6b, 0x12d: 0x71, 0x12e: 0x72, 0x12f: 0x73, + 0x131: 0x74, 0x132: 0x75, 0x133: 0x76, 0x134: 0x77, 0x135: 0x78, 0x137: 0x79, + 0x138: 0x7a, 0x139: 0x7b, 0x13a: 0x7c, 0x13b: 0x7d, 0x13c: 0x7e, 0x13d: 0x7f, 0x13e: 0x80, 0x13f: 0x81, + // Block 0x5, offset 0x140 + 0x140: 0x82, 0x142: 0x83, 0x143: 0x84, 0x144: 0x85, 0x145: 0x86, 0x146: 0x87, 0x147: 0x88, + 0x14d: 0x89, + 0x15c: 0x8a, 0x15f: 0x8b, + 0x162: 0x8c, 0x164: 0x8d, + 0x168: 0x8e, 0x169: 0x8f, 0x16a: 0x90, 0x16c: 0x0e, 0x16d: 0x91, 0x16e: 0x92, 0x16f: 0x93, + 0x170: 0x94, 0x173: 0x95, 0x174: 0x96, 0x175: 0x0f, 0x176: 0x10, 0x177: 0x97, + 0x178: 0x11, 0x179: 0x12, 0x17a: 0x13, 0x17b: 0x14, 0x17c: 0x15, 0x17d: 0x16, 0x17e: 0x17, 0x17f: 0x18, + // Block 0x6, offset 0x180 + 0x180: 0x98, 0x181: 0x99, 0x182: 0x9a, 0x183: 0x9b, 0x184: 0x19, 0x185: 0x1a, 0x186: 0x9c, 0x187: 0x9d, + 0x188: 0x9e, 0x189: 0x1b, 0x18a: 0x1c, 0x18b: 0x9f, 0x18c: 0xa0, + 0x191: 0x1d, 0x192: 0x1e, 0x193: 0xa1, + 0x1a8: 0xa2, 0x1a9: 0xa3, 0x1ab: 0xa4, + 0x1b1: 0xa5, 0x1b3: 0xa6, 0x1b5: 0xa7, 0x1b7: 0xa8, + 0x1ba: 0xa9, 0x1bb: 0xaa, 0x1bc: 0x1f, 0x1bd: 0x20, 0x1be: 0x21, 0x1bf: 0xab, + // Block 0x7, offset 0x1c0 + 0x1c0: 0xac, 0x1c1: 0x22, 0x1c2: 0x23, 0x1c3: 0x24, 0x1c4: 0xad, 0x1c5: 0x25, 0x1c6: 0x26, + 0x1c8: 0x27, 0x1c9: 0x28, 0x1ca: 0x29, 0x1cb: 0x2a, 0x1cc: 0x2b, 0x1cd: 0x2c, 0x1ce: 0x2d, 0x1cf: 0x2e, + // Block 0x8, offset 0x200 + 0x219: 0xae, 0x21a: 0xaf, 0x21b: 0xb0, 0x21d: 0xb1, 0x21f: 0xb2, + 0x220: 0xb3, 0x223: 0xb4, 0x224: 0xb5, 0x225: 0xb6, 0x226: 0xb7, 0x227: 0xb8, + 0x22a: 0xb9, 0x22b: 0xba, 0x22d: 0xbb, 0x22f: 0xbc, + 0x230: 0xbd, 0x231: 0xbe, 0x232: 0xbf, 0x233: 0xc0, 0x234: 0xc1, 0x235: 0xc2, 0x236: 0xc3, 0x237: 0xbd, + 0x238: 0xbe, 0x239: 0xbf, 0x23a: 0xc0, 0x23b: 0xc1, 0x23c: 0xc2, 0x23d: 0xc3, 0x23e: 0xbd, 0x23f: 0xbe, + // Block 0x9, offset 0x240 + 0x240: 0xbf, 0x241: 0xc0, 0x242: 0xc1, 0x243: 0xc2, 0x244: 0xc3, 0x245: 0xbd, 0x246: 0xbe, 0x247: 0xbf, + 0x248: 0xc0, 0x249: 0xc1, 0x24a: 0xc2, 0x24b: 0xc3, 0x24c: 0xbd, 0x24d: 0xbe, 0x24e: 0xbf, 0x24f: 0xc0, + 0x250: 0xc1, 0x251: 0xc2, 0x252: 0xc3, 0x253: 0xbd, 0x254: 0xbe, 0x255: 0xbf, 0x256: 0xc0, 0x257: 0xc1, + 0x258: 0xc2, 0x259: 0xc3, 0x25a: 0xbd, 0x25b: 0xbe, 0x25c: 0xbf, 0x25d: 0xc0, 0x25e: 0xc1, 0x25f: 0xc2, + 0x260: 0xc3, 0x261: 0xbd, 0x262: 0xbe, 0x263: 0xbf, 0x264: 0xc0, 0x265: 0xc1, 0x266: 0xc2, 0x267: 0xc3, + 0x268: 0xbd, 0x269: 0xbe, 0x26a: 0xbf, 0x26b: 0xc0, 0x26c: 0xc1, 0x26d: 0xc2, 0x26e: 0xc3, 0x26f: 0xbd, + 0x270: 0xbe, 0x271: 0xbf, 0x272: 0xc0, 0x273: 0xc1, 0x274: 0xc2, 0x275: 0xc3, 0x276: 0xbd, 0x277: 0xbe, + 0x278: 0xbf, 0x279: 0xc0, 0x27a: 0xc1, 0x27b: 0xc2, 0x27c: 0xc3, 0x27d: 0xbd, 0x27e: 0xbe, 0x27f: 0xbf, + // Block 0xa, offset 0x280 + 0x280: 0xc0, 0x281: 0xc1, 0x282: 0xc2, 0x283: 0xc3, 0x284: 0xbd, 0x285: 0xbe, 0x286: 0xbf, 0x287: 0xc0, + 0x288: 0xc1, 0x289: 0xc2, 0x28a: 0xc3, 0x28b: 0xbd, 0x28c: 0xbe, 0x28d: 0xbf, 0x28e: 0xc0, 0x28f: 0xc1, + 0x290: 0xc2, 0x291: 0xc3, 0x292: 0xbd, 0x293: 0xbe, 0x294: 0xbf, 0x295: 0xc0, 0x296: 0xc1, 0x297: 0xc2, + 0x298: 0xc3, 0x299: 0xbd, 0x29a: 0xbe, 0x29b: 0xbf, 0x29c: 0xc0, 0x29d: 0xc1, 0x29e: 0xc2, 0x29f: 0xc3, + 0x2a0: 0xbd, 0x2a1: 0xbe, 0x2a2: 0xbf, 0x2a3: 0xc0, 0x2a4: 0xc1, 0x2a5: 0xc2, 0x2a6: 0xc3, 0x2a7: 0xbd, + 0x2a8: 0xbe, 0x2a9: 0xbf, 0x2aa: 0xc0, 0x2ab: 0xc1, 0x2ac: 0xc2, 0x2ad: 0xc3, 0x2ae: 0xbd, 0x2af: 0xbe, + 0x2b0: 0xbf, 0x2b1: 0xc0, 0x2b2: 0xc1, 0x2b3: 0xc2, 0x2b4: 0xc3, 0x2b5: 0xbd, 0x2b6: 0xbe, 0x2b7: 0xbf, + 0x2b8: 0xc0, 0x2b9: 0xc1, 0x2ba: 0xc2, 0x2bb: 0xc3, 0x2bc: 0xbd, 0x2bd: 0xbe, 0x2be: 0xbf, 0x2bf: 0xc0, + // Block 0xb, offset 0x2c0 + 0x2c0: 0xc1, 0x2c1: 0xc2, 0x2c2: 0xc3, 0x2c3: 0xbd, 0x2c4: 0xbe, 0x2c5: 0xbf, 0x2c6: 0xc0, 0x2c7: 0xc1, + 0x2c8: 0xc2, 0x2c9: 0xc3, 0x2ca: 0xbd, 0x2cb: 0xbe, 0x2cc: 0xbf, 0x2cd: 0xc0, 0x2ce: 0xc1, 0x2cf: 0xc2, + 0x2d0: 0xc3, 0x2d1: 0xbd, 0x2d2: 0xbe, 0x2d3: 0xbf, 0x2d4: 0xc0, 0x2d5: 0xc1, 0x2d6: 0xc2, 0x2d7: 0xc3, + 0x2d8: 0xbd, 0x2d9: 0xbe, 0x2da: 0xbf, 0x2db: 0xc0, 0x2dc: 0xc1, 0x2dd: 0xc2, 0x2de: 0xc4, + // Block 0xc, offset 0x300 + 0x324: 0x2f, 0x325: 0x30, 0x326: 0x31, 0x327: 0x32, + 0x328: 0x33, 0x329: 0x34, 0x32a: 0x35, 0x32b: 0x36, 0x32c: 0x37, 0x32d: 0x38, 0x32e: 0x39, 0x32f: 0x3a, + 0x330: 0x3b, 0x331: 0x3c, 0x332: 0x3d, 0x333: 0x3e, 0x334: 0x3f, 0x335: 0x40, 0x336: 0x41, 0x337: 0x42, + 0x338: 0x43, 0x339: 0x44, 0x33a: 0x45, 0x33b: 0x46, 0x33c: 0xc5, 0x33d: 0x47, 0x33e: 0x48, 0x33f: 0x49, + // Block 0xd, offset 0x340 + 0x347: 0xc6, + 0x34b: 0xc7, 0x34d: 0xc8, + 0x368: 0xc9, 0x36b: 0xca, + // Block 0xe, offset 0x380 + 0x381: 0xcb, 0x382: 0xcc, 0x384: 0xcd, 0x385: 0xb7, 0x387: 0xce, + 0x388: 0xcf, 0x38b: 0xd0, 0x38c: 0x6b, 0x38d: 0xd1, + 0x392: 0xd2, 0x393: 0xd3, 0x396: 0xd4, 0x397: 0xd5, + 0x398: 0xd6, 0x39a: 0xd7, 0x39c: 0xd8, + // Block 0xf, offset 0x3c0 + 0x3eb: 0xd9, 0x3ec: 0xda, + // Block 0x10, offset 0x400 + 0x432: 0xdb, + // Block 0x11, offset 0x440 + 0x445: 0xdc, 0x446: 0xdd, 0x447: 0xde, + 0x449: 0xdf, + 0x450: 0xe0, 0x451: 0xe1, 0x452: 0xe2, 0x453: 0xe3, 0x454: 0xe4, 0x455: 0xe5, 0x456: 0xe6, 0x457: 0xe7, + 0x458: 0xe8, 0x459: 0xe9, 0x45a: 0x4a, 0x45b: 0xea, 0x45c: 0xeb, 0x45d: 0xec, 0x45e: 0xed, 0x45f: 0x4b, + // Block 0x12, offset 0x480 + 0x4a3: 0xee, + 0x4b8: 0x4c, 0x4b9: 0x4d, 0x4ba: 0x4e, + // Block 0x13, offset 0x4c0 + 0x4c4: 0x4f, 0x4c5: 0xef, 0x4c6: 0xf0, + 0x4c8: 0x50, 0x4c9: 0xf1, + // Block 0x14, offset 0x500 + 0x520: 0x51, 0x521: 0x52, 0x522: 0x53, 0x523: 0x54, 0x524: 0x55, 0x525: 0x56, 0x526: 0x57, 0x527: 0x58, + 0x528: 0x59, + // Block 0x15, offset 0x540 + 0x550: 0x0b, 0x551: 0x0c, 0x556: 0x0d, + 0x55b: 0x0e, 0x55d: 0x0f, 0x55e: 0x10, 0x55f: 0x11, + 0x56f: 0x12, +} + +// nfkcSparseOffset: 152 entries, 304 bytes +var nfkcSparseOffset = []uint16{0x0, 0xe, 0x12, 0x1b, 0x25, 0x35, 0x37, 0x3c, 0x47, 0x56, 0x63, 0x6b, 0x6f, 0x74, 0x76, 0x86, 0x8e, 0x95, 0x98, 0x9f, 0xa3, 0xa7, 0xa9, 0xab, 0xb4, 0xb8, 0xbf, 0xc4, 0xc7, 0xd1, 0xd3, 0xda, 0xe2, 0xe6, 0xe8, 0xeb, 0xef, 0xf5, 0x106, 0x112, 0x114, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12b, 0x12e, 0x130, 0x133, 0x136, 0x13a, 0x13f, 0x148, 0x14a, 0x14d, 0x14f, 0x15a, 0x165, 0x174, 0x182, 0x190, 0x1a0, 0x1ae, 0x1b5, 0x1bb, 0x1ca, 0x1ce, 0x1d0, 0x1d4, 0x1d6, 0x1d9, 0x1db, 0x1de, 0x1e0, 0x1e3, 0x1e5, 0x1e7, 0x1e9, 0x1f5, 0x1ff, 0x209, 0x20c, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21b, 0x21d, 0x21f, 0x221, 0x223, 0x229, 0x22c, 0x230, 0x232, 0x239, 0x23f, 0x245, 0x24d, 0x253, 0x259, 0x25f, 0x263, 0x265, 0x267, 0x269, 0x26b, 0x271, 0x274, 0x277, 0x27f, 0x286, 0x289, 0x28c, 0x28e, 0x296, 0x29d, 0x2a0, 0x2a6, 0x2a8, 0x2aa, 0x2ad, 0x2af, 0x2b1, 0x2b3, 0x2b5, 0x2c2, 0x2cc, 0x2ce, 0x2d0, 0x2d4, 0x2d9, 0x2e5, 0x2ea, 0x2f3, 0x2f9, 0x2fe, 0x302, 0x307, 0x30b, 0x31b, 0x329, 0x337, 0x345, 0x347, 0x351, 0x353} + +// nfkcSparseValues: 861 entries, 3444 bytes +var nfkcSparseValues = [861]valueRange{ + // Block 0x0, offset 0x0 + {value: 0x0002, lo: 0x0d}, + {value: 0x0001, lo: 0xa0, hi: 0xa0}, + {value: 0x4274, lo: 0xa8, hi: 0xa8}, + {value: 0x0083, lo: 0xaa, hi: 0xaa}, + {value: 0x4260, lo: 0xaf, hi: 0xaf}, + {value: 0x0025, lo: 0xb2, hi: 0xb3}, + {value: 0x4256, lo: 0xb4, hi: 0xb4}, + {value: 0x01dc, lo: 0xb5, hi: 0xb5}, + {value: 0x428d, lo: 0xb8, hi: 0xb8}, + {value: 0x0023, lo: 0xb9, hi: 0xb9}, + {value: 0x009f, lo: 0xba, hi: 0xba}, + {value: 0x2218, lo: 0xbc, hi: 0xbc}, + {value: 0x220c, lo: 0xbd, hi: 0xbd}, + {value: 0x22ae, lo: 0xbe, hi: 0xbe}, + // Block 0x1, offset 0xe + {value: 0x0091, lo: 0x03}, + {value: 0x4774, lo: 0xa0, hi: 0xa1}, + {value: 0x47a6, lo: 0xaf, hi: 0xb0}, + {value: 0xa000, lo: 0xb7, hi: 0xb7}, + // Block 0x2, offset 0x12 + {value: 0x0003, lo: 0x08}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0x0091, lo: 0xb0, hi: 0xb0}, + {value: 0x0119, lo: 0xb1, hi: 0xb1}, + {value: 0x0095, lo: 0xb2, hi: 0xb2}, + {value: 0x00a5, lo: 0xb3, hi: 0xb3}, + {value: 0x0143, lo: 0xb4, hi: 0xb6}, + {value: 0x00af, lo: 0xb7, hi: 0xb7}, + {value: 0x00b3, lo: 0xb8, hi: 0xb8}, + // Block 0x3, offset 0x1b + {value: 0x000a, lo: 0x09}, + {value: 0x426a, lo: 0x98, hi: 0x98}, + {value: 0x426f, lo: 0x99, hi: 0x9a}, + {value: 0x4292, lo: 0x9b, hi: 0x9b}, + {value: 0x425b, lo: 0x9c, hi: 0x9c}, + {value: 0x427e, lo: 0x9d, hi: 0x9d}, + {value: 0x0113, lo: 0xa0, hi: 0xa0}, + {value: 0x0099, lo: 0xa1, hi: 0xa1}, + {value: 0x00a7, lo: 0xa2, hi: 0xa3}, + {value: 0x0167, lo: 0xa4, hi: 0xa4}, + // Block 0x4, offset 0x25 + {value: 0x0000, lo: 0x0f}, + {value: 0xa000, lo: 0x83, hi: 0x83}, + {value: 0xa000, lo: 0x87, hi: 0x87}, + {value: 0xa000, lo: 0x8b, hi: 0x8b}, + {value: 0xa000, lo: 0x8d, hi: 0x8d}, + {value: 0x37a1, lo: 0x90, hi: 0x90}, + {value: 0x37ad, lo: 0x91, hi: 0x91}, + {value: 0x379b, lo: 0x93, hi: 0x93}, + {value: 0xa000, lo: 0x96, hi: 0x96}, + {value: 0x3813, lo: 0x97, hi: 0x97}, + {value: 0x37dd, lo: 0x9c, hi: 0x9c}, + {value: 0x37c5, lo: 0x9d, hi: 0x9d}, + {value: 0x37ef, lo: 0x9e, hi: 0x9e}, + {value: 0xa000, lo: 0xb4, hi: 0xb5}, + {value: 0x3819, lo: 0xb6, hi: 0xb6}, + {value: 0x381f, lo: 0xb7, hi: 0xb7}, + // Block 0x5, offset 0x35 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0x83, hi: 0x87}, + // Block 0x6, offset 0x37 + {value: 0x0001, lo: 0x04}, + {value: 0x8113, lo: 0x81, hi: 0x82}, + {value: 0x8132, lo: 0x84, hi: 0x84}, + {value: 0x812d, lo: 0x85, hi: 0x85}, + {value: 0x810d, lo: 0x87, hi: 0x87}, + // Block 0x7, offset 0x3c + {value: 0x0000, lo: 0x0a}, + {value: 0x8132, lo: 0x90, hi: 0x97}, + {value: 0x8119, lo: 0x98, hi: 0x98}, + {value: 0x811a, lo: 0x99, hi: 0x99}, + {value: 0x811b, lo: 0x9a, hi: 0x9a}, + {value: 0x383d, lo: 0xa2, hi: 0xa2}, + {value: 0x3843, lo: 0xa3, hi: 0xa3}, + {value: 0x384f, lo: 0xa4, hi: 0xa4}, + {value: 0x3849, lo: 0xa5, hi: 0xa5}, + {value: 0x3855, lo: 0xa6, hi: 0xa6}, + {value: 0xa000, lo: 0xa7, hi: 0xa7}, + // Block 0x8, offset 0x47 + {value: 0x0000, lo: 0x0e}, + {value: 0x3867, lo: 0x80, hi: 0x80}, + {value: 0xa000, lo: 0x81, hi: 0x81}, + {value: 0x385b, lo: 0x82, hi: 0x82}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0x3861, lo: 0x93, hi: 0x93}, + {value: 0xa000, lo: 0x95, hi: 0x95}, + {value: 0x8132, lo: 0x96, hi: 0x9c}, + {value: 0x8132, lo: 0x9f, hi: 0xa2}, + {value: 0x812d, lo: 0xa3, hi: 0xa3}, + {value: 0x8132, lo: 0xa4, hi: 0xa4}, + {value: 0x8132, lo: 0xa7, hi: 0xa8}, + {value: 0x812d, lo: 0xaa, hi: 0xaa}, + {value: 0x8132, lo: 0xab, hi: 0xac}, + {value: 0x812d, lo: 0xad, hi: 0xad}, + // Block 0x9, offset 0x56 + {value: 0x0000, lo: 0x0c}, + {value: 0x811f, lo: 0x91, hi: 0x91}, + {value: 0x8132, lo: 0xb0, hi: 0xb0}, + {value: 0x812d, lo: 0xb1, hi: 0xb1}, + {value: 0x8132, lo: 0xb2, hi: 0xb3}, + {value: 0x812d, lo: 0xb4, hi: 0xb4}, + {value: 0x8132, lo: 0xb5, hi: 0xb6}, + {value: 0x812d, lo: 0xb7, hi: 0xb9}, + {value: 0x8132, lo: 0xba, hi: 0xba}, + {value: 0x812d, lo: 0xbb, hi: 0xbc}, + {value: 0x8132, lo: 0xbd, hi: 0xbd}, + {value: 0x812d, lo: 0xbe, hi: 0xbe}, + {value: 0x8132, lo: 0xbf, hi: 0xbf}, + // Block 0xa, offset 0x63 + {value: 0x0005, lo: 0x07}, + {value: 0x8132, lo: 0x80, hi: 0x80}, + {value: 0x8132, lo: 0x81, hi: 0x81}, + {value: 0x812d, lo: 0x82, hi: 0x83}, + {value: 0x812d, lo: 0x84, hi: 0x85}, + {value: 0x812d, lo: 0x86, hi: 0x87}, + {value: 0x812d, lo: 0x88, hi: 0x89}, + {value: 0x8132, lo: 0x8a, hi: 0x8a}, + // Block 0xb, offset 0x6b + {value: 0x0000, lo: 0x03}, + {value: 0x8132, lo: 0xab, hi: 0xb1}, + {value: 0x812d, lo: 0xb2, hi: 0xb2}, + {value: 0x8132, lo: 0xb3, hi: 0xb3}, + // Block 0xc, offset 0x6f + {value: 0x0000, lo: 0x04}, + {value: 0x8132, lo: 0x96, hi: 0x99}, + {value: 0x8132, lo: 0x9b, hi: 0xa3}, + {value: 0x8132, lo: 0xa5, hi: 0xa7}, + {value: 0x8132, lo: 0xa9, hi: 0xad}, + // Block 0xd, offset 0x74 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0x99, hi: 0x9b}, + // Block 0xe, offset 0x76 + {value: 0x0000, lo: 0x0f}, + {value: 0x812d, lo: 0xa3, hi: 0xa3}, + {value: 0x8132, lo: 0xa4, hi: 0xa5}, + {value: 0x812d, lo: 0xa6, hi: 0xa6}, + {value: 0x8132, lo: 0xa7, hi: 0xa8}, + {value: 0x812d, lo: 0xa9, hi: 0xa9}, + {value: 0x8132, lo: 0xaa, hi: 0xac}, + {value: 0x812d, lo: 0xad, hi: 0xaf}, + {value: 0x8116, lo: 0xb0, hi: 0xb0}, + {value: 0x8117, lo: 0xb1, hi: 0xb1}, + {value: 0x8118, lo: 0xb2, hi: 0xb2}, + {value: 0x8132, lo: 0xb3, hi: 0xb5}, + {value: 0x812d, lo: 0xb6, hi: 0xb6}, + {value: 0x8132, lo: 0xb7, hi: 0xb8}, + {value: 0x812d, lo: 0xb9, hi: 0xba}, + {value: 0x8132, lo: 0xbb, hi: 0xbf}, + // Block 0xf, offset 0x86 + {value: 0x0000, lo: 0x07}, + {value: 0xa000, lo: 0xa8, hi: 0xa8}, + {value: 0x3ed4, lo: 0xa9, hi: 0xa9}, + {value: 0xa000, lo: 0xb0, hi: 0xb0}, + {value: 0x3edc, lo: 0xb1, hi: 0xb1}, + {value: 0xa000, lo: 0xb3, hi: 0xb3}, + {value: 0x3ee4, lo: 0xb4, hi: 0xb4}, + {value: 0x9902, lo: 0xbc, hi: 0xbc}, + // Block 0x10, offset 0x8e + {value: 0x0008, lo: 0x06}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x8132, lo: 0x91, hi: 0x91}, + {value: 0x812d, lo: 0x92, hi: 0x92}, + {value: 0x8132, lo: 0x93, hi: 0x93}, + {value: 0x8132, lo: 0x94, hi: 0x94}, + {value: 0x45ae, lo: 0x98, hi: 0x9f}, + // Block 0x11, offset 0x95 + {value: 0x0000, lo: 0x02}, + {value: 0x8102, lo: 0xbc, hi: 0xbc}, + {value: 0x9900, lo: 0xbe, hi: 0xbe}, + // Block 0x12, offset 0x98 + {value: 0x0008, lo: 0x06}, + {value: 0xa000, lo: 0x87, hi: 0x87}, + {value: 0x2c9a, lo: 0x8b, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x97, hi: 0x97}, + {value: 0x45ee, lo: 0x9c, hi: 0x9d}, + {value: 0x45fe, lo: 0x9f, hi: 0x9f}, + // Block 0x13, offset 0x9f + {value: 0x0000, lo: 0x03}, + {value: 0x4626, lo: 0xb3, hi: 0xb3}, + {value: 0x462e, lo: 0xb6, hi: 0xb6}, + {value: 0x8102, lo: 0xbc, hi: 0xbc}, + // Block 0x14, offset 0xa3 + {value: 0x0008, lo: 0x03}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x4606, lo: 0x99, hi: 0x9b}, + {value: 0x461e, lo: 0x9e, hi: 0x9e}, + // Block 0x15, offset 0xa7 + {value: 0x0000, lo: 0x01}, + {value: 0x8102, lo: 0xbc, hi: 0xbc}, + // Block 0x16, offset 0xa9 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + // Block 0x17, offset 0xab + {value: 0x0000, lo: 0x08}, + {value: 0xa000, lo: 0x87, hi: 0x87}, + {value: 0x2cb2, lo: 0x88, hi: 0x88}, + {value: 0x2caa, lo: 0x8b, hi: 0x8b}, + {value: 0x2cba, lo: 0x8c, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x96, hi: 0x97}, + {value: 0x4636, lo: 0x9c, hi: 0x9c}, + {value: 0x463e, lo: 0x9d, hi: 0x9d}, + // Block 0x18, offset 0xb4 + {value: 0x0000, lo: 0x03}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0x2cc2, lo: 0x94, hi: 0x94}, + {value: 0x9900, lo: 0xbe, hi: 0xbe}, + // Block 0x19, offset 0xb8 + {value: 0x0000, lo: 0x06}, + {value: 0xa000, lo: 0x86, hi: 0x87}, + {value: 0x2cca, lo: 0x8a, hi: 0x8a}, + {value: 0x2cda, lo: 0x8b, hi: 0x8b}, + {value: 0x2cd2, lo: 0x8c, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x97, hi: 0x97}, + // Block 0x1a, offset 0xbf + {value: 0x1801, lo: 0x04}, + {value: 0xa000, lo: 0x86, hi: 0x86}, + {value: 0x3eec, lo: 0x88, hi: 0x88}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x8120, lo: 0x95, hi: 0x96}, + // Block 0x1b, offset 0xc4 + {value: 0x0000, lo: 0x02}, + {value: 0x8102, lo: 0xbc, hi: 0xbc}, + {value: 0xa000, lo: 0xbf, hi: 0xbf}, + // Block 0x1c, offset 0xc7 + {value: 0x0000, lo: 0x09}, + {value: 0x2ce2, lo: 0x80, hi: 0x80}, + {value: 0x9900, lo: 0x82, hi: 0x82}, + {value: 0xa000, lo: 0x86, hi: 0x86}, + {value: 0x2cea, lo: 0x87, hi: 0x87}, + {value: 0x2cf2, lo: 0x88, hi: 0x88}, + {value: 0x2f4c, lo: 0x8a, hi: 0x8a}, + {value: 0x2dd4, lo: 0x8b, hi: 0x8b}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x95, hi: 0x96}, + // Block 0x1d, offset 0xd1 + {value: 0x0000, lo: 0x01}, + {value: 0x9900, lo: 0xbe, hi: 0xbe}, + // Block 0x1e, offset 0xd3 + {value: 0x0000, lo: 0x06}, + {value: 0xa000, lo: 0x86, hi: 0x87}, + {value: 0x2cfa, lo: 0x8a, hi: 0x8a}, + {value: 0x2d0a, lo: 0x8b, hi: 0x8b}, + {value: 0x2d02, lo: 0x8c, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x97, hi: 0x97}, + // Block 0x1f, offset 0xda + {value: 0x6bee, lo: 0x07}, + {value: 0x9904, lo: 0x8a, hi: 0x8a}, + {value: 0x9900, lo: 0x8f, hi: 0x8f}, + {value: 0xa000, lo: 0x99, hi: 0x99}, + {value: 0x3ef4, lo: 0x9a, hi: 0x9a}, + {value: 0x2f54, lo: 0x9c, hi: 0x9c}, + {value: 0x2ddf, lo: 0x9d, hi: 0x9d}, + {value: 0x2d12, lo: 0x9e, hi: 0x9f}, + // Block 0x20, offset 0xe2 + {value: 0x0000, lo: 0x03}, + {value: 0x261d, lo: 0xb3, hi: 0xb3}, + {value: 0x8122, lo: 0xb8, hi: 0xb9}, + {value: 0x8104, lo: 0xba, hi: 0xba}, + // Block 0x21, offset 0xe6 + {value: 0x0000, lo: 0x01}, + {value: 0x8123, lo: 0x88, hi: 0x8b}, + // Block 0x22, offset 0xe8 + {value: 0x0000, lo: 0x02}, + {value: 0x2632, lo: 0xb3, hi: 0xb3}, + {value: 0x8124, lo: 0xb8, hi: 0xb9}, + // Block 0x23, offset 0xeb + {value: 0x0000, lo: 0x03}, + {value: 0x8125, lo: 0x88, hi: 0x8b}, + {value: 0x2624, lo: 0x9c, hi: 0x9c}, + {value: 0x262b, lo: 0x9d, hi: 0x9d}, + // Block 0x24, offset 0xef + {value: 0x0000, lo: 0x05}, + {value: 0x030b, lo: 0x8c, hi: 0x8c}, + {value: 0x812d, lo: 0x98, hi: 0x99}, + {value: 0x812d, lo: 0xb5, hi: 0xb5}, + {value: 0x812d, lo: 0xb7, hi: 0xb7}, + {value: 0x812b, lo: 0xb9, hi: 0xb9}, + // Block 0x25, offset 0xf5 + {value: 0x0000, lo: 0x10}, + {value: 0x2640, lo: 0x83, hi: 0x83}, + {value: 0x2647, lo: 0x8d, hi: 0x8d}, + {value: 0x264e, lo: 0x92, hi: 0x92}, + {value: 0x2655, lo: 0x97, hi: 0x97}, + {value: 0x265c, lo: 0x9c, hi: 0x9c}, + {value: 0x2639, lo: 0xa9, hi: 0xa9}, + {value: 0x8126, lo: 0xb1, hi: 0xb1}, + {value: 0x8127, lo: 0xb2, hi: 0xb2}, + {value: 0x4a62, lo: 0xb3, hi: 0xb3}, + {value: 0x8128, lo: 0xb4, hi: 0xb4}, + {value: 0x4a6b, lo: 0xb5, hi: 0xb5}, + {value: 0x4646, lo: 0xb6, hi: 0xb6}, + {value: 0x4686, lo: 0xb7, hi: 0xb7}, + {value: 0x464e, lo: 0xb8, hi: 0xb8}, + {value: 0x4691, lo: 0xb9, hi: 0xb9}, + {value: 0x8127, lo: 0xba, hi: 0xbd}, + // Block 0x26, offset 0x106 + {value: 0x0000, lo: 0x0b}, + {value: 0x8127, lo: 0x80, hi: 0x80}, + {value: 0x4a74, lo: 0x81, hi: 0x81}, + {value: 0x8132, lo: 0x82, hi: 0x83}, + {value: 0x8104, lo: 0x84, hi: 0x84}, + {value: 0x8132, lo: 0x86, hi: 0x87}, + {value: 0x266a, lo: 0x93, hi: 0x93}, + {value: 0x2671, lo: 0x9d, hi: 0x9d}, + {value: 0x2678, lo: 0xa2, hi: 0xa2}, + {value: 0x267f, lo: 0xa7, hi: 0xa7}, + {value: 0x2686, lo: 0xac, hi: 0xac}, + {value: 0x2663, lo: 0xb9, hi: 0xb9}, + // Block 0x27, offset 0x112 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0x86, hi: 0x86}, + // Block 0x28, offset 0x114 + {value: 0x0000, lo: 0x05}, + {value: 0xa000, lo: 0xa5, hi: 0xa5}, + {value: 0x2d1a, lo: 0xa6, hi: 0xa6}, + {value: 0x9900, lo: 0xae, hi: 0xae}, + {value: 0x8102, lo: 0xb7, hi: 0xb7}, + {value: 0x8104, lo: 0xb9, hi: 0xba}, + // Block 0x29, offset 0x11a + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0x8d, hi: 0x8d}, + // Block 0x2a, offset 0x11c + {value: 0x0000, lo: 0x01}, + {value: 0x030f, lo: 0xbc, hi: 0xbc}, + // Block 0x2b, offset 0x11e + {value: 0x0000, lo: 0x01}, + {value: 0xa000, lo: 0x80, hi: 0x92}, + // Block 0x2c, offset 0x120 + {value: 0x0000, lo: 0x01}, + {value: 0xb900, lo: 0xa1, hi: 0xb5}, + // Block 0x2d, offset 0x122 + {value: 0x0000, lo: 0x01}, + {value: 0x9900, lo: 0xa8, hi: 0xbf}, + // Block 0x2e, offset 0x124 + {value: 0x0000, lo: 0x01}, + {value: 0x9900, lo: 0x80, hi: 0x82}, + // Block 0x2f, offset 0x126 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0x9d, hi: 0x9f}, + // Block 0x30, offset 0x128 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x94, hi: 0x94}, + {value: 0x8104, lo: 0xb4, hi: 0xb4}, + // Block 0x31, offset 0x12b + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x92, hi: 0x92}, + {value: 0x8132, lo: 0x9d, hi: 0x9d}, + // Block 0x32, offset 0x12e + {value: 0x0000, lo: 0x01}, + {value: 0x8131, lo: 0xa9, hi: 0xa9}, + // Block 0x33, offset 0x130 + {value: 0x0004, lo: 0x02}, + {value: 0x812e, lo: 0xb9, hi: 0xba}, + {value: 0x812d, lo: 0xbb, hi: 0xbb}, + // Block 0x34, offset 0x133 + {value: 0x0000, lo: 0x02}, + {value: 0x8132, lo: 0x97, hi: 0x97}, + {value: 0x812d, lo: 0x98, hi: 0x98}, + // Block 0x35, offset 0x136 + {value: 0x0000, lo: 0x03}, + {value: 0x8104, lo: 0xa0, hi: 0xa0}, + {value: 0x8132, lo: 0xb5, hi: 0xbc}, + {value: 0x812d, lo: 0xbf, hi: 0xbf}, + // Block 0x36, offset 0x13a + {value: 0x0000, lo: 0x04}, + {value: 0x8132, lo: 0xb0, hi: 0xb4}, + {value: 0x812d, lo: 0xb5, hi: 0xba}, + {value: 0x8132, lo: 0xbb, hi: 0xbc}, + {value: 0x812d, lo: 0xbd, hi: 0xbd}, + // Block 0x37, offset 0x13f + {value: 0x0000, lo: 0x08}, + {value: 0x2d62, lo: 0x80, hi: 0x80}, + {value: 0x2d6a, lo: 0x81, hi: 0x81}, + {value: 0xa000, lo: 0x82, hi: 0x82}, + {value: 0x2d72, lo: 0x83, hi: 0x83}, + {value: 0x8104, lo: 0x84, hi: 0x84}, + {value: 0x8132, lo: 0xab, hi: 0xab}, + {value: 0x812d, lo: 0xac, hi: 0xac}, + {value: 0x8132, lo: 0xad, hi: 0xb3}, + // Block 0x38, offset 0x148 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0xaa, hi: 0xab}, + // Block 0x39, offset 0x14a + {value: 0x0000, lo: 0x02}, + {value: 0x8102, lo: 0xa6, hi: 0xa6}, + {value: 0x8104, lo: 0xb2, hi: 0xb3}, + // Block 0x3a, offset 0x14d + {value: 0x0000, lo: 0x01}, + {value: 0x8102, lo: 0xb7, hi: 0xb7}, + // Block 0x3b, offset 0x14f + {value: 0x0000, lo: 0x0a}, + {value: 0x8132, lo: 0x90, hi: 0x92}, + {value: 0x8101, lo: 0x94, hi: 0x94}, + {value: 0x812d, lo: 0x95, hi: 0x99}, + {value: 0x8132, lo: 0x9a, hi: 0x9b}, + {value: 0x812d, lo: 0x9c, hi: 0x9f}, + {value: 0x8132, lo: 0xa0, hi: 0xa0}, + {value: 0x8101, lo: 0xa2, hi: 0xa8}, + {value: 0x812d, lo: 0xad, hi: 0xad}, + {value: 0x8132, lo: 0xb4, hi: 0xb4}, + {value: 0x8132, lo: 0xb8, hi: 0xb9}, + // Block 0x3c, offset 0x15a + {value: 0x0002, lo: 0x0a}, + {value: 0x0043, lo: 0xac, hi: 0xac}, + {value: 0x00d1, lo: 0xad, hi: 0xad}, + {value: 0x0045, lo: 0xae, hi: 0xae}, + {value: 0x0049, lo: 0xb0, hi: 0xb1}, + {value: 0x00e6, lo: 0xb2, hi: 0xb2}, + {value: 0x004f, lo: 0xb3, hi: 0xba}, + {value: 0x005f, lo: 0xbc, hi: 0xbc}, + {value: 0x00ef, lo: 0xbd, hi: 0xbd}, + {value: 0x0061, lo: 0xbe, hi: 0xbe}, + {value: 0x0065, lo: 0xbf, hi: 0xbf}, + // Block 0x3d, offset 0x165 + {value: 0x0000, lo: 0x0e}, + {value: 0x8132, lo: 0x80, hi: 0x81}, + {value: 0x812d, lo: 0x82, hi: 0x82}, + {value: 0x8132, lo: 0x83, hi: 0x89}, + {value: 0x812d, lo: 0x8a, hi: 0x8a}, + {value: 0x8132, lo: 0x8b, hi: 0x8c}, + {value: 0x8135, lo: 0x8d, hi: 0x8d}, + {value: 0x812a, lo: 0x8e, hi: 0x8e}, + {value: 0x812d, lo: 0x8f, hi: 0x8f}, + {value: 0x8129, lo: 0x90, hi: 0x90}, + {value: 0x8132, lo: 0x91, hi: 0xb5}, + {value: 0x8134, lo: 0xbc, hi: 0xbc}, + {value: 0x812d, lo: 0xbd, hi: 0xbd}, + {value: 0x8132, lo: 0xbe, hi: 0xbe}, + {value: 0x812d, lo: 0xbf, hi: 0xbf}, + // Block 0x3e, offset 0x174 + {value: 0x0000, lo: 0x0d}, + {value: 0x0001, lo: 0x80, hi: 0x8a}, + {value: 0x043b, lo: 0x91, hi: 0x91}, + {value: 0x4297, lo: 0x97, hi: 0x97}, + {value: 0x001d, lo: 0xa4, hi: 0xa4}, + {value: 0x186f, lo: 0xa5, hi: 0xa5}, + {value: 0x1b58, lo: 0xa6, hi: 0xa6}, + {value: 0x0001, lo: 0xaf, hi: 0xaf}, + {value: 0x268d, lo: 0xb3, hi: 0xb3}, + {value: 0x27fa, lo: 0xb4, hi: 0xb4}, + {value: 0x2694, lo: 0xb6, hi: 0xb6}, + {value: 0x2804, lo: 0xb7, hi: 0xb7}, + {value: 0x1869, lo: 0xbc, hi: 0xbc}, + {value: 0x4265, lo: 0xbe, hi: 0xbe}, + // Block 0x3f, offset 0x182 + {value: 0x0002, lo: 0x0d}, + {value: 0x192f, lo: 0x87, hi: 0x87}, + {value: 0x192c, lo: 0x88, hi: 0x88}, + {value: 0x186c, lo: 0x89, hi: 0x89}, + {value: 0x298a, lo: 0x97, hi: 0x97}, + {value: 0x0001, lo: 0x9f, hi: 0x9f}, + {value: 0x0021, lo: 0xb0, hi: 0xb0}, + {value: 0x0093, lo: 0xb1, hi: 0xb1}, + {value: 0x0029, lo: 0xb4, hi: 0xb9}, + {value: 0x0017, lo: 0xba, hi: 0xba}, + {value: 0x0467, lo: 0xbb, hi: 0xbb}, + {value: 0x003b, lo: 0xbc, hi: 0xbc}, + {value: 0x0011, lo: 0xbd, hi: 0xbe}, + {value: 0x009d, lo: 0xbf, hi: 0xbf}, + // Block 0x40, offset 0x190 + {value: 0x0002, lo: 0x0f}, + {value: 0x0021, lo: 0x80, hi: 0x89}, + {value: 0x0017, lo: 0x8a, hi: 0x8a}, + {value: 0x0467, lo: 0x8b, hi: 0x8b}, + {value: 0x003b, lo: 0x8c, hi: 0x8c}, + {value: 0x0011, lo: 0x8d, hi: 0x8e}, + {value: 0x0083, lo: 0x90, hi: 0x90}, + {value: 0x008b, lo: 0x91, hi: 0x91}, + {value: 0x009f, lo: 0x92, hi: 0x92}, + {value: 0x00b1, lo: 0x93, hi: 0x93}, + {value: 0x0104, lo: 0x94, hi: 0x94}, + {value: 0x0091, lo: 0x95, hi: 0x95}, + {value: 0x0097, lo: 0x96, hi: 0x99}, + {value: 0x00a1, lo: 0x9a, hi: 0x9a}, + {value: 0x00a7, lo: 0x9b, hi: 0x9c}, + {value: 0x1995, lo: 0xa8, hi: 0xa8}, + // Block 0x41, offset 0x1a0 + {value: 0x0000, lo: 0x0d}, + {value: 0x8132, lo: 0x90, hi: 0x91}, + {value: 0x8101, lo: 0x92, hi: 0x93}, + {value: 0x8132, lo: 0x94, hi: 0x97}, + {value: 0x8101, lo: 0x98, hi: 0x9a}, + {value: 0x8132, lo: 0x9b, hi: 0x9c}, + {value: 0x8132, lo: 0xa1, hi: 0xa1}, + {value: 0x8101, lo: 0xa5, hi: 0xa6}, + {value: 0x8132, lo: 0xa7, hi: 0xa7}, + {value: 0x812d, lo: 0xa8, hi: 0xa8}, + {value: 0x8132, lo: 0xa9, hi: 0xa9}, + {value: 0x8101, lo: 0xaa, hi: 0xab}, + {value: 0x812d, lo: 0xac, hi: 0xaf}, + {value: 0x8132, lo: 0xb0, hi: 0xb0}, + // Block 0x42, offset 0x1ae + {value: 0x0007, lo: 0x06}, + {value: 0x217c, lo: 0x89, hi: 0x89}, + {value: 0xa000, lo: 0x90, hi: 0x90}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0xa000, lo: 0x94, hi: 0x94}, + {value: 0x3bb5, lo: 0x9a, hi: 0x9b}, + {value: 0x3bc3, lo: 0xae, hi: 0xae}, + // Block 0x43, offset 0x1b5 + {value: 0x000e, lo: 0x05}, + {value: 0x3bca, lo: 0x8d, hi: 0x8e}, + {value: 0x3bd1, lo: 0x8f, hi: 0x8f}, + {value: 0xa000, lo: 0x90, hi: 0x90}, + {value: 0xa000, lo: 0x92, hi: 0x92}, + {value: 0xa000, lo: 0x94, hi: 0x94}, + // Block 0x44, offset 0x1bb + {value: 0x0173, lo: 0x0e}, + {value: 0xa000, lo: 0x83, hi: 0x83}, + {value: 0x3bdf, lo: 0x84, hi: 0x84}, + {value: 0xa000, lo: 0x88, hi: 0x88}, + {value: 0x3be6, lo: 0x89, hi: 0x89}, + {value: 0xa000, lo: 0x8b, hi: 0x8b}, + {value: 0x3bed, lo: 0x8c, hi: 0x8c}, + {value: 0xa000, lo: 0xa3, hi: 0xa3}, + {value: 0x3bf4, lo: 0xa4, hi: 0xa4}, + {value: 0xa000, lo: 0xa5, hi: 0xa5}, + {value: 0x3bfb, lo: 0xa6, hi: 0xa6}, + {value: 0x269b, lo: 0xac, hi: 0xad}, + {value: 0x26a2, lo: 0xaf, hi: 0xaf}, + {value: 0x2818, lo: 0xb0, hi: 0xb0}, + {value: 0xa000, lo: 0xbc, hi: 0xbc}, + // Block 0x45, offset 0x1ca + {value: 0x0007, lo: 0x03}, + {value: 0x3c64, lo: 0xa0, hi: 0xa1}, + {value: 0x3c8e, lo: 0xa2, hi: 0xa3}, + {value: 0x3cb8, lo: 0xaa, hi: 0xad}, + // Block 0x46, offset 0x1ce + {value: 0x0004, lo: 0x01}, + {value: 0x048b, lo: 0xa9, hi: 0xaa}, + // Block 0x47, offset 0x1d0 + {value: 0x0002, lo: 0x03}, + {value: 0x0057, lo: 0x80, hi: 0x8f}, + {value: 0x0083, lo: 0x90, hi: 0xa9}, + {value: 0x0021, lo: 0xaa, hi: 0xaa}, + // Block 0x48, offset 0x1d4 + {value: 0x0000, lo: 0x01}, + {value: 0x2997, lo: 0x8c, hi: 0x8c}, + // Block 0x49, offset 0x1d6 + {value: 0x0263, lo: 0x02}, + {value: 0x1b88, lo: 0xb4, hi: 0xb4}, + {value: 0x1929, lo: 0xb5, hi: 0xb6}, + // Block 0x4a, offset 0x1d9 + {value: 0x0000, lo: 0x01}, + {value: 0x456f, lo: 0x9c, hi: 0x9c}, + // Block 0x4b, offset 0x1db + {value: 0x0000, lo: 0x02}, + {value: 0x0095, lo: 0xbc, hi: 0xbc}, + {value: 0x006d, lo: 0xbd, hi: 0xbd}, + // Block 0x4c, offset 0x1de + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xaf, hi: 0xb1}, + // Block 0x4d, offset 0x1e0 + {value: 0x0000, lo: 0x02}, + {value: 0x047f, lo: 0xaf, hi: 0xaf}, + {value: 0x8104, lo: 0xbf, hi: 0xbf}, + // Block 0x4e, offset 0x1e3 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xa0, hi: 0xbf}, + // Block 0x4f, offset 0x1e5 + {value: 0x0000, lo: 0x01}, + {value: 0x0dc3, lo: 0x9f, hi: 0x9f}, + // Block 0x50, offset 0x1e7 + {value: 0x0000, lo: 0x01}, + {value: 0x162b, lo: 0xb3, hi: 0xb3}, + // Block 0x51, offset 0x1e9 + {value: 0x0004, lo: 0x0b}, + {value: 0x1593, lo: 0x80, hi: 0x82}, + {value: 0x15ab, lo: 0x83, hi: 0x83}, + {value: 0x15c3, lo: 0x84, hi: 0x85}, + {value: 0x15d3, lo: 0x86, hi: 0x89}, + {value: 0x15e7, lo: 0x8a, hi: 0x8c}, + {value: 0x15fb, lo: 0x8d, hi: 0x8d}, + {value: 0x1603, lo: 0x8e, hi: 0x8e}, + {value: 0x160b, lo: 0x8f, hi: 0x90}, + {value: 0x1617, lo: 0x91, hi: 0x93}, + {value: 0x1627, lo: 0x94, hi: 0x94}, + {value: 0x162f, lo: 0x95, hi: 0x95}, + // Block 0x52, offset 0x1f5 + {value: 0x0004, lo: 0x09}, + {value: 0x0001, lo: 0x80, hi: 0x80}, + {value: 0x812c, lo: 0xaa, hi: 0xaa}, + {value: 0x8131, lo: 0xab, hi: 0xab}, + {value: 0x8133, lo: 0xac, hi: 0xac}, + {value: 0x812e, lo: 0xad, hi: 0xad}, + {value: 0x812f, lo: 0xae, hi: 0xae}, + {value: 0x812f, lo: 0xaf, hi: 0xaf}, + {value: 0x04b3, lo: 0xb6, hi: 0xb6}, + {value: 0x0887, lo: 0xb8, hi: 0xba}, + // Block 0x53, offset 0x1ff + {value: 0x0005, lo: 0x09}, + {value: 0x0313, lo: 0xb1, hi: 0xb1}, + {value: 0x0317, lo: 0xb2, hi: 0xb2}, + {value: 0x4341, lo: 0xb3, hi: 0xb3}, + {value: 0x031b, lo: 0xb4, hi: 0xb4}, + {value: 0x4346, lo: 0xb5, hi: 0xb6}, + {value: 0x031f, lo: 0xb7, hi: 0xb7}, + {value: 0x0323, lo: 0xb8, hi: 0xb8}, + {value: 0x0327, lo: 0xb9, hi: 0xb9}, + {value: 0x4350, lo: 0xba, hi: 0xbf}, + // Block 0x54, offset 0x209 + {value: 0x0000, lo: 0x02}, + {value: 0x8132, lo: 0xaf, hi: 0xaf}, + {value: 0x8132, lo: 0xb4, hi: 0xbd}, + // Block 0x55, offset 0x20c + {value: 0x0000, lo: 0x03}, + {value: 0x020f, lo: 0x9c, hi: 0x9c}, + {value: 0x0212, lo: 0x9d, hi: 0x9d}, + {value: 0x8132, lo: 0x9e, hi: 0x9f}, + // Block 0x56, offset 0x210 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xb0, hi: 0xb1}, + // Block 0x57, offset 0x212 + {value: 0x0000, lo: 0x01}, + {value: 0x1637, lo: 0xb0, hi: 0xb0}, + // Block 0x58, offset 0x214 + {value: 0x000c, lo: 0x01}, + {value: 0x00d7, lo: 0xb8, hi: 0xb9}, + // Block 0x59, offset 0x216 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0x86, hi: 0x86}, + // Block 0x5a, offset 0x218 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x84, hi: 0x84}, + {value: 0x8132, lo: 0xa0, hi: 0xb1}, + // Block 0x5b, offset 0x21b + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0xab, hi: 0xad}, + // Block 0x5c, offset 0x21d + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0x93, hi: 0x93}, + // Block 0x5d, offset 0x21f + {value: 0x0000, lo: 0x01}, + {value: 0x8102, lo: 0xb3, hi: 0xb3}, + // Block 0x5e, offset 0x221 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0x80, hi: 0x80}, + // Block 0x5f, offset 0x223 + {value: 0x0000, lo: 0x05}, + {value: 0x8132, lo: 0xb0, hi: 0xb0}, + {value: 0x8132, lo: 0xb2, hi: 0xb3}, + {value: 0x812d, lo: 0xb4, hi: 0xb4}, + {value: 0x8132, lo: 0xb7, hi: 0xb8}, + {value: 0x8132, lo: 0xbe, hi: 0xbf}, + // Block 0x60, offset 0x229 + {value: 0x0000, lo: 0x02}, + {value: 0x8132, lo: 0x81, hi: 0x81}, + {value: 0x8104, lo: 0xb6, hi: 0xb6}, + // Block 0x61, offset 0x22c + {value: 0x0008, lo: 0x03}, + {value: 0x1633, lo: 0x9c, hi: 0x9d}, + {value: 0x0125, lo: 0x9e, hi: 0x9e}, + {value: 0x163f, lo: 0x9f, hi: 0x9f}, + // Block 0x62, offset 0x230 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0xad, hi: 0xad}, + // Block 0x63, offset 0x232 + {value: 0x0000, lo: 0x06}, + {value: 0xe500, lo: 0x80, hi: 0x80}, + {value: 0xc600, lo: 0x81, hi: 0x9b}, + {value: 0xe500, lo: 0x9c, hi: 0x9c}, + {value: 0xc600, lo: 0x9d, hi: 0xb7}, + {value: 0xe500, lo: 0xb8, hi: 0xb8}, + {value: 0xc600, lo: 0xb9, hi: 0xbf}, + // Block 0x64, offset 0x239 + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x93}, + {value: 0xe500, lo: 0x94, hi: 0x94}, + {value: 0xc600, lo: 0x95, hi: 0xaf}, + {value: 0xe500, lo: 0xb0, hi: 0xb0}, + {value: 0xc600, lo: 0xb1, hi: 0xbf}, + // Block 0x65, offset 0x23f + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x8b}, + {value: 0xe500, lo: 0x8c, hi: 0x8c}, + {value: 0xc600, lo: 0x8d, hi: 0xa7}, + {value: 0xe500, lo: 0xa8, hi: 0xa8}, + {value: 0xc600, lo: 0xa9, hi: 0xbf}, + // Block 0x66, offset 0x245 + {value: 0x0000, lo: 0x07}, + {value: 0xc600, lo: 0x80, hi: 0x83}, + {value: 0xe500, lo: 0x84, hi: 0x84}, + {value: 0xc600, lo: 0x85, hi: 0x9f}, + {value: 0xe500, lo: 0xa0, hi: 0xa0}, + {value: 0xc600, lo: 0xa1, hi: 0xbb}, + {value: 0xe500, lo: 0xbc, hi: 0xbc}, + {value: 0xc600, lo: 0xbd, hi: 0xbf}, + // Block 0x67, offset 0x24d + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x97}, + {value: 0xe500, lo: 0x98, hi: 0x98}, + {value: 0xc600, lo: 0x99, hi: 0xb3}, + {value: 0xe500, lo: 0xb4, hi: 0xb4}, + {value: 0xc600, lo: 0xb5, hi: 0xbf}, + // Block 0x68, offset 0x253 + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x8f}, + {value: 0xe500, lo: 0x90, hi: 0x90}, + {value: 0xc600, lo: 0x91, hi: 0xab}, + {value: 0xe500, lo: 0xac, hi: 0xac}, + {value: 0xc600, lo: 0xad, hi: 0xbf}, + // Block 0x69, offset 0x259 + {value: 0x0000, lo: 0x05}, + {value: 0xc600, lo: 0x80, hi: 0x87}, + {value: 0xe500, lo: 0x88, hi: 0x88}, + {value: 0xc600, lo: 0x89, hi: 0xa3}, + {value: 0xe500, lo: 0xa4, hi: 0xa4}, + {value: 0xc600, lo: 0xa5, hi: 0xbf}, + // Block 0x6a, offset 0x25f + {value: 0x0000, lo: 0x03}, + {value: 0xc600, lo: 0x80, hi: 0x87}, + {value: 0xe500, lo: 0x88, hi: 0x88}, + {value: 0xc600, lo: 0x89, hi: 0xa3}, + // Block 0x6b, offset 0x263 + {value: 0x0002, lo: 0x01}, + {value: 0x0003, lo: 0x81, hi: 0xbf}, + // Block 0x6c, offset 0x265 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0xbd, hi: 0xbd}, + // Block 0x6d, offset 0x267 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0xa0, hi: 0xa0}, + // Block 0x6e, offset 0x269 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xb6, hi: 0xba}, + // Block 0x6f, offset 0x26b + {value: 0x002c, lo: 0x05}, + {value: 0x812d, lo: 0x8d, hi: 0x8d}, + {value: 0x8132, lo: 0x8f, hi: 0x8f}, + {value: 0x8132, lo: 0xb8, hi: 0xb8}, + {value: 0x8101, lo: 0xb9, hi: 0xba}, + {value: 0x8104, lo: 0xbf, hi: 0xbf}, + // Block 0x70, offset 0x271 + {value: 0x0000, lo: 0x02}, + {value: 0x8132, lo: 0xa5, hi: 0xa5}, + {value: 0x812d, lo: 0xa6, hi: 0xa6}, + // Block 0x71, offset 0x274 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x86, hi: 0x86}, + {value: 0x8104, lo: 0xbf, hi: 0xbf}, + // Block 0x72, offset 0x277 + {value: 0x17fe, lo: 0x07}, + {value: 0xa000, lo: 0x99, hi: 0x99}, + {value: 0x4234, lo: 0x9a, hi: 0x9a}, + {value: 0xa000, lo: 0x9b, hi: 0x9b}, + {value: 0x423e, lo: 0x9c, hi: 0x9c}, + {value: 0xa000, lo: 0xa5, hi: 0xa5}, + {value: 0x4248, lo: 0xab, hi: 0xab}, + {value: 0x8104, lo: 0xb9, hi: 0xba}, + // Block 0x73, offset 0x27f + {value: 0x0000, lo: 0x06}, + {value: 0x8132, lo: 0x80, hi: 0x82}, + {value: 0x9900, lo: 0xa7, hi: 0xa7}, + {value: 0x2d7a, lo: 0xae, hi: 0xae}, + {value: 0x2d84, lo: 0xaf, hi: 0xaf}, + {value: 0xa000, lo: 0xb1, hi: 0xb2}, + {value: 0x8104, lo: 0xb3, hi: 0xb4}, + // Block 0x74, offset 0x286 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x80, hi: 0x80}, + {value: 0x8102, lo: 0x8a, hi: 0x8a}, + // Block 0x75, offset 0x289 + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0xb5, hi: 0xb5}, + {value: 0x8102, lo: 0xb6, hi: 0xb6}, + // Block 0x76, offset 0x28c + {value: 0x0002, lo: 0x01}, + {value: 0x8102, lo: 0xa9, hi: 0xaa}, + // Block 0x77, offset 0x28e + {value: 0x0000, lo: 0x07}, + {value: 0xa000, lo: 0x87, hi: 0x87}, + {value: 0x2d8e, lo: 0x8b, hi: 0x8b}, + {value: 0x2d98, lo: 0x8c, hi: 0x8c}, + {value: 0x8104, lo: 0x8d, hi: 0x8d}, + {value: 0x9900, lo: 0x97, hi: 0x97}, + {value: 0x8132, lo: 0xa6, hi: 0xac}, + {value: 0x8132, lo: 0xb0, hi: 0xb4}, + // Block 0x78, offset 0x296 + {value: 0x6b5e, lo: 0x06}, + {value: 0x9900, lo: 0xb0, hi: 0xb0}, + {value: 0xa000, lo: 0xb9, hi: 0xb9}, + {value: 0x9900, lo: 0xba, hi: 0xba}, + {value: 0x2dac, lo: 0xbb, hi: 0xbb}, + {value: 0x2da2, lo: 0xbc, hi: 0xbd}, + {value: 0x2db6, lo: 0xbe, hi: 0xbe}, + // Block 0x79, offset 0x29d + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0x82, hi: 0x82}, + {value: 0x8102, lo: 0x83, hi: 0x83}, + // Block 0x7a, offset 0x2a0 + {value: 0x0000, lo: 0x05}, + {value: 0x9900, lo: 0xaf, hi: 0xaf}, + {value: 0xa000, lo: 0xb8, hi: 0xb9}, + {value: 0x2dc0, lo: 0xba, hi: 0xba}, + {value: 0x2dca, lo: 0xbb, hi: 0xbb}, + {value: 0x8104, lo: 0xbf, hi: 0xbf}, + // Block 0x7b, offset 0x2a6 + {value: 0x0000, lo: 0x01}, + {value: 0x8102, lo: 0x80, hi: 0x80}, + // Block 0x7c, offset 0x2a8 + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0xbf, hi: 0xbf}, + // Block 0x7d, offset 0x2aa + {value: 0x0000, lo: 0x02}, + {value: 0x8104, lo: 0xb6, hi: 0xb6}, + {value: 0x8102, lo: 0xb7, hi: 0xb7}, + // Block 0x7e, offset 0x2ad + {value: 0x0000, lo: 0x01}, + {value: 0x8104, lo: 0xab, hi: 0xab}, + // Block 0x7f, offset 0x2af + {value: 0x0000, lo: 0x01}, + {value: 0x8101, lo: 0xb0, hi: 0xb4}, + // Block 0x80, offset 0x2b1 + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0xb0, hi: 0xb6}, + // Block 0x81, offset 0x2b3 + {value: 0x0000, lo: 0x01}, + {value: 0x8101, lo: 0x9e, hi: 0x9e}, + // Block 0x82, offset 0x2b5 + {value: 0x0000, lo: 0x0c}, + {value: 0x465e, lo: 0x9e, hi: 0x9e}, + {value: 0x4668, lo: 0x9f, hi: 0x9f}, + {value: 0x469c, lo: 0xa0, hi: 0xa0}, + {value: 0x46aa, lo: 0xa1, hi: 0xa1}, + {value: 0x46b8, lo: 0xa2, hi: 0xa2}, + {value: 0x46c6, lo: 0xa3, hi: 0xa3}, + {value: 0x46d4, lo: 0xa4, hi: 0xa4}, + {value: 0x812b, lo: 0xa5, hi: 0xa6}, + {value: 0x8101, lo: 0xa7, hi: 0xa9}, + {value: 0x8130, lo: 0xad, hi: 0xad}, + {value: 0x812b, lo: 0xae, hi: 0xb2}, + {value: 0x812d, lo: 0xbb, hi: 0xbf}, + // Block 0x83, offset 0x2c2 + {value: 0x0000, lo: 0x09}, + {value: 0x812d, lo: 0x80, hi: 0x82}, + {value: 0x8132, lo: 0x85, hi: 0x89}, + {value: 0x812d, lo: 0x8a, hi: 0x8b}, + {value: 0x8132, lo: 0xaa, hi: 0xad}, + {value: 0x4672, lo: 0xbb, hi: 0xbb}, + {value: 0x467c, lo: 0xbc, hi: 0xbc}, + {value: 0x46e2, lo: 0xbd, hi: 0xbd}, + {value: 0x46fe, lo: 0xbe, hi: 0xbe}, + {value: 0x46f0, lo: 0xbf, hi: 0xbf}, + // Block 0x84, offset 0x2cc + {value: 0x0000, lo: 0x01}, + {value: 0x470c, lo: 0x80, hi: 0x80}, + // Block 0x85, offset 0x2ce + {value: 0x0000, lo: 0x01}, + {value: 0x8132, lo: 0x82, hi: 0x84}, + // Block 0x86, offset 0x2d0 + {value: 0x0002, lo: 0x03}, + {value: 0x0043, lo: 0x80, hi: 0x99}, + {value: 0x0083, lo: 0x9a, hi: 0xb3}, + {value: 0x0043, lo: 0xb4, hi: 0xbf}, + // Block 0x87, offset 0x2d4 + {value: 0x0002, lo: 0x04}, + {value: 0x005b, lo: 0x80, hi: 0x8d}, + {value: 0x0083, lo: 0x8e, hi: 0x94}, + {value: 0x0093, lo: 0x96, hi: 0xa7}, + {value: 0x0043, lo: 0xa8, hi: 0xbf}, + // Block 0x88, offset 0x2d9 + {value: 0x0002, lo: 0x0b}, + {value: 0x0073, lo: 0x80, hi: 0x81}, + {value: 0x0083, lo: 0x82, hi: 0x9b}, + {value: 0x0043, lo: 0x9c, hi: 0x9c}, + {value: 0x0047, lo: 0x9e, hi: 0x9f}, + {value: 0x004f, lo: 0xa2, hi: 0xa2}, + {value: 0x0055, lo: 0xa5, hi: 0xa6}, + {value: 0x005d, lo: 0xa9, hi: 0xac}, + {value: 0x0067, lo: 0xae, hi: 0xb5}, + {value: 0x0083, lo: 0xb6, hi: 0xb9}, + {value: 0x008d, lo: 0xbb, hi: 0xbb}, + {value: 0x0091, lo: 0xbd, hi: 0xbf}, + // Block 0x89, offset 0x2e5 + {value: 0x0002, lo: 0x04}, + {value: 0x0097, lo: 0x80, hi: 0x83}, + {value: 0x00a1, lo: 0x85, hi: 0x8f}, + {value: 0x0043, lo: 0x90, hi: 0xa9}, + {value: 0x0083, lo: 0xaa, hi: 0xbf}, + // Block 0x8a, offset 0x2ea + {value: 0x0002, lo: 0x08}, + {value: 0x00af, lo: 0x80, hi: 0x83}, + {value: 0x0043, lo: 0x84, hi: 0x85}, + {value: 0x0049, lo: 0x87, hi: 0x8a}, + {value: 0x0055, lo: 0x8d, hi: 0x94}, + {value: 0x0067, lo: 0x96, hi: 0x9c}, + {value: 0x0083, lo: 0x9e, hi: 0xb7}, + {value: 0x0043, lo: 0xb8, hi: 0xb9}, + {value: 0x0049, lo: 0xbb, hi: 0xbe}, + // Block 0x8b, offset 0x2f3 + {value: 0x0002, lo: 0x05}, + {value: 0x0053, lo: 0x80, hi: 0x84}, + {value: 0x005f, lo: 0x86, hi: 0x86}, + {value: 0x0067, lo: 0x8a, hi: 0x90}, + {value: 0x0083, lo: 0x92, hi: 0xab}, + {value: 0x0043, lo: 0xac, hi: 0xbf}, + // Block 0x8c, offset 0x2f9 + {value: 0x0002, lo: 0x04}, + {value: 0x006b, lo: 0x80, hi: 0x85}, + {value: 0x0083, lo: 0x86, hi: 0x9f}, + {value: 0x0043, lo: 0xa0, hi: 0xb9}, + {value: 0x0083, lo: 0xba, hi: 0xbf}, + // Block 0x8d, offset 0x2fe + {value: 0x0002, lo: 0x03}, + {value: 0x008f, lo: 0x80, hi: 0x93}, + {value: 0x0043, lo: 0x94, hi: 0xad}, + {value: 0x0083, lo: 0xae, hi: 0xbf}, + // Block 0x8e, offset 0x302 + {value: 0x0002, lo: 0x04}, + {value: 0x00a7, lo: 0x80, hi: 0x87}, + {value: 0x0043, lo: 0x88, hi: 0xa1}, + {value: 0x0083, lo: 0xa2, hi: 0xbb}, + {value: 0x0043, lo: 0xbc, hi: 0xbf}, + // Block 0x8f, offset 0x307 + {value: 0x0002, lo: 0x03}, + {value: 0x004b, lo: 0x80, hi: 0x95}, + {value: 0x0083, lo: 0x96, hi: 0xaf}, + {value: 0x0043, lo: 0xb0, hi: 0xbf}, + // Block 0x90, offset 0x30b + {value: 0x0003, lo: 0x0f}, + {value: 0x01b8, lo: 0x80, hi: 0x80}, + {value: 0x045f, lo: 0x81, hi: 0x81}, + {value: 0x01bb, lo: 0x82, hi: 0x9a}, + {value: 0x045b, lo: 0x9b, hi: 0x9b}, + {value: 0x01c7, lo: 0x9c, hi: 0x9c}, + {value: 0x01d0, lo: 0x9d, hi: 0x9d}, + {value: 0x01d6, lo: 0x9e, hi: 0x9e}, + {value: 0x01fa, lo: 0x9f, hi: 0x9f}, + {value: 0x01eb, lo: 0xa0, hi: 0xa0}, + {value: 0x01e8, lo: 0xa1, hi: 0xa1}, + {value: 0x0173, lo: 0xa2, hi: 0xb2}, + {value: 0x0188, lo: 0xb3, hi: 0xb3}, + {value: 0x01a6, lo: 0xb4, hi: 0xba}, + {value: 0x045f, lo: 0xbb, hi: 0xbb}, + {value: 0x01bb, lo: 0xbc, hi: 0xbf}, + // Block 0x91, offset 0x31b + {value: 0x0003, lo: 0x0d}, + {value: 0x01c7, lo: 0x80, hi: 0x94}, + {value: 0x045b, lo: 0x95, hi: 0x95}, + {value: 0x01c7, lo: 0x96, hi: 0x96}, + {value: 0x01d0, lo: 0x97, hi: 0x97}, + {value: 0x01d6, lo: 0x98, hi: 0x98}, + {value: 0x01fa, lo: 0x99, hi: 0x99}, + {value: 0x01eb, lo: 0x9a, hi: 0x9a}, + {value: 0x01e8, lo: 0x9b, hi: 0x9b}, + {value: 0x0173, lo: 0x9c, hi: 0xac}, + {value: 0x0188, lo: 0xad, hi: 0xad}, + {value: 0x01a6, lo: 0xae, hi: 0xb4}, + {value: 0x045f, lo: 0xb5, hi: 0xb5}, + {value: 0x01bb, lo: 0xb6, hi: 0xbf}, + // Block 0x92, offset 0x329 + {value: 0x0003, lo: 0x0d}, + {value: 0x01d9, lo: 0x80, hi: 0x8e}, + {value: 0x045b, lo: 0x8f, hi: 0x8f}, + {value: 0x01c7, lo: 0x90, hi: 0x90}, + {value: 0x01d0, lo: 0x91, hi: 0x91}, + {value: 0x01d6, lo: 0x92, hi: 0x92}, + {value: 0x01fa, lo: 0x93, hi: 0x93}, + {value: 0x01eb, lo: 0x94, hi: 0x94}, + {value: 0x01e8, lo: 0x95, hi: 0x95}, + {value: 0x0173, lo: 0x96, hi: 0xa6}, + {value: 0x0188, lo: 0xa7, hi: 0xa7}, + {value: 0x01a6, lo: 0xa8, hi: 0xae}, + {value: 0x045f, lo: 0xaf, hi: 0xaf}, + {value: 0x01bb, lo: 0xb0, hi: 0xbf}, + // Block 0x93, offset 0x337 + {value: 0x0003, lo: 0x0d}, + {value: 0x01eb, lo: 0x80, hi: 0x88}, + {value: 0x045b, lo: 0x89, hi: 0x89}, + {value: 0x01c7, lo: 0x8a, hi: 0x8a}, + {value: 0x01d0, lo: 0x8b, hi: 0x8b}, + {value: 0x01d6, lo: 0x8c, hi: 0x8c}, + {value: 0x01fa, lo: 0x8d, hi: 0x8d}, + {value: 0x01eb, lo: 0x8e, hi: 0x8e}, + {value: 0x01e8, lo: 0x8f, hi: 0x8f}, + {value: 0x0173, lo: 0x90, hi: 0xa0}, + {value: 0x0188, lo: 0xa1, hi: 0xa1}, + {value: 0x01a6, lo: 0xa2, hi: 0xa8}, + {value: 0x045f, lo: 0xa9, hi: 0xa9}, + {value: 0x01bb, lo: 0xaa, hi: 0xbf}, + // Block 0x94, offset 0x345 + {value: 0x0000, lo: 0x01}, + {value: 0x812d, lo: 0x90, hi: 0x96}, + // Block 0x95, offset 0x347 + {value: 0x0002, lo: 0x09}, + {value: 0x0063, lo: 0x80, hi: 0x89}, + {value: 0x194d, lo: 0x8a, hi: 0x8a}, + {value: 0x197d, lo: 0x8b, hi: 0x8b}, + {value: 0x1998, lo: 0x8c, hi: 0x8c}, + {value: 0x199e, lo: 0x8d, hi: 0x8d}, + {value: 0x1bbc, lo: 0x8e, hi: 0x8e}, + {value: 0x19aa, lo: 0x8f, hi: 0x8f}, + {value: 0x1977, lo: 0xaa, hi: 0xaa}, + {value: 0x197a, lo: 0xab, hi: 0xab}, + // Block 0x96, offset 0x351 + {value: 0x0000, lo: 0x01}, + {value: 0x193b, lo: 0x90, hi: 0x90}, + // Block 0x97, offset 0x353 + {value: 0x0028, lo: 0x09}, + {value: 0x285e, lo: 0x80, hi: 0x80}, + {value: 0x2822, lo: 0x81, hi: 0x81}, + {value: 0x282c, lo: 0x82, hi: 0x82}, + {value: 0x2840, lo: 0x83, hi: 0x84}, + {value: 0x284a, lo: 0x85, hi: 0x86}, + {value: 0x2836, lo: 0x87, hi: 0x87}, + {value: 0x2854, lo: 0x88, hi: 0x88}, + {value: 0x0b6f, lo: 0x90, hi: 0x90}, + {value: 0x08e7, lo: 0x91, hi: 0x91}, +} + +// recompMap: 7520 bytes (entries only) +var recompMap = map[uint32]rune{ + 0x00410300: 0x00C0, + 0x00410301: 0x00C1, + 0x00410302: 0x00C2, + 0x00410303: 0x00C3, + 0x00410308: 0x00C4, + 0x0041030A: 0x00C5, + 0x00430327: 0x00C7, + 0x00450300: 0x00C8, + 0x00450301: 0x00C9, + 0x00450302: 0x00CA, + 0x00450308: 0x00CB, + 0x00490300: 0x00CC, + 0x00490301: 0x00CD, + 0x00490302: 0x00CE, + 0x00490308: 0x00CF, + 0x004E0303: 0x00D1, + 0x004F0300: 0x00D2, + 0x004F0301: 0x00D3, + 0x004F0302: 0x00D4, + 0x004F0303: 0x00D5, + 0x004F0308: 0x00D6, + 0x00550300: 0x00D9, + 0x00550301: 0x00DA, + 0x00550302: 0x00DB, + 0x00550308: 0x00DC, + 0x00590301: 0x00DD, + 0x00610300: 0x00E0, + 0x00610301: 0x00E1, + 0x00610302: 0x00E2, + 0x00610303: 0x00E3, + 0x00610308: 0x00E4, + 0x0061030A: 0x00E5, + 0x00630327: 0x00E7, + 0x00650300: 0x00E8, + 0x00650301: 0x00E9, + 0x00650302: 0x00EA, + 0x00650308: 0x00EB, + 0x00690300: 0x00EC, + 0x00690301: 0x00ED, + 0x00690302: 0x00EE, + 0x00690308: 0x00EF, + 0x006E0303: 0x00F1, + 0x006F0300: 0x00F2, + 0x006F0301: 0x00F3, + 0x006F0302: 0x00F4, + 0x006F0303: 0x00F5, + 0x006F0308: 0x00F6, + 0x00750300: 0x00F9, + 0x00750301: 0x00FA, + 0x00750302: 0x00FB, + 0x00750308: 0x00FC, + 0x00790301: 0x00FD, + 0x00790308: 0x00FF, + 0x00410304: 0x0100, + 0x00610304: 0x0101, + 0x00410306: 0x0102, + 0x00610306: 0x0103, + 0x00410328: 0x0104, + 0x00610328: 0x0105, + 0x00430301: 0x0106, + 0x00630301: 0x0107, + 0x00430302: 0x0108, + 0x00630302: 0x0109, + 0x00430307: 0x010A, + 0x00630307: 0x010B, + 0x0043030C: 0x010C, + 0x0063030C: 0x010D, + 0x0044030C: 0x010E, + 0x0064030C: 0x010F, + 0x00450304: 0x0112, + 0x00650304: 0x0113, + 0x00450306: 0x0114, + 0x00650306: 0x0115, + 0x00450307: 0x0116, + 0x00650307: 0x0117, + 0x00450328: 0x0118, + 0x00650328: 0x0119, + 0x0045030C: 0x011A, + 0x0065030C: 0x011B, + 0x00470302: 0x011C, + 0x00670302: 0x011D, + 0x00470306: 0x011E, + 0x00670306: 0x011F, + 0x00470307: 0x0120, + 0x00670307: 0x0121, + 0x00470327: 0x0122, + 0x00670327: 0x0123, + 0x00480302: 0x0124, + 0x00680302: 0x0125, + 0x00490303: 0x0128, + 0x00690303: 0x0129, + 0x00490304: 0x012A, + 0x00690304: 0x012B, + 0x00490306: 0x012C, + 0x00690306: 0x012D, + 0x00490328: 0x012E, + 0x00690328: 0x012F, + 0x00490307: 0x0130, + 0x004A0302: 0x0134, + 0x006A0302: 0x0135, + 0x004B0327: 0x0136, + 0x006B0327: 0x0137, + 0x004C0301: 0x0139, + 0x006C0301: 0x013A, + 0x004C0327: 0x013B, + 0x006C0327: 0x013C, + 0x004C030C: 0x013D, + 0x006C030C: 0x013E, + 0x004E0301: 0x0143, + 0x006E0301: 0x0144, + 0x004E0327: 0x0145, + 0x006E0327: 0x0146, + 0x004E030C: 0x0147, + 0x006E030C: 0x0148, + 0x004F0304: 0x014C, + 0x006F0304: 0x014D, + 0x004F0306: 0x014E, + 0x006F0306: 0x014F, + 0x004F030B: 0x0150, + 0x006F030B: 0x0151, + 0x00520301: 0x0154, + 0x00720301: 0x0155, + 0x00520327: 0x0156, + 0x00720327: 0x0157, + 0x0052030C: 0x0158, + 0x0072030C: 0x0159, + 0x00530301: 0x015A, + 0x00730301: 0x015B, + 0x00530302: 0x015C, + 0x00730302: 0x015D, + 0x00530327: 0x015E, + 0x00730327: 0x015F, + 0x0053030C: 0x0160, + 0x0073030C: 0x0161, + 0x00540327: 0x0162, + 0x00740327: 0x0163, + 0x0054030C: 0x0164, + 0x0074030C: 0x0165, + 0x00550303: 0x0168, + 0x00750303: 0x0169, + 0x00550304: 0x016A, + 0x00750304: 0x016B, + 0x00550306: 0x016C, + 0x00750306: 0x016D, + 0x0055030A: 0x016E, + 0x0075030A: 0x016F, + 0x0055030B: 0x0170, + 0x0075030B: 0x0171, + 0x00550328: 0x0172, + 0x00750328: 0x0173, + 0x00570302: 0x0174, + 0x00770302: 0x0175, + 0x00590302: 0x0176, + 0x00790302: 0x0177, + 0x00590308: 0x0178, + 0x005A0301: 0x0179, + 0x007A0301: 0x017A, + 0x005A0307: 0x017B, + 0x007A0307: 0x017C, + 0x005A030C: 0x017D, + 0x007A030C: 0x017E, + 0x004F031B: 0x01A0, + 0x006F031B: 0x01A1, + 0x0055031B: 0x01AF, + 0x0075031B: 0x01B0, + 0x0041030C: 0x01CD, + 0x0061030C: 0x01CE, + 0x0049030C: 0x01CF, + 0x0069030C: 0x01D0, + 0x004F030C: 0x01D1, + 0x006F030C: 0x01D2, + 0x0055030C: 0x01D3, + 0x0075030C: 0x01D4, + 0x00DC0304: 0x01D5, + 0x00FC0304: 0x01D6, + 0x00DC0301: 0x01D7, + 0x00FC0301: 0x01D8, + 0x00DC030C: 0x01D9, + 0x00FC030C: 0x01DA, + 0x00DC0300: 0x01DB, + 0x00FC0300: 0x01DC, + 0x00C40304: 0x01DE, + 0x00E40304: 0x01DF, + 0x02260304: 0x01E0, + 0x02270304: 0x01E1, + 0x00C60304: 0x01E2, + 0x00E60304: 0x01E3, + 0x0047030C: 0x01E6, + 0x0067030C: 0x01E7, + 0x004B030C: 0x01E8, + 0x006B030C: 0x01E9, + 0x004F0328: 0x01EA, + 0x006F0328: 0x01EB, + 0x01EA0304: 0x01EC, + 0x01EB0304: 0x01ED, + 0x01B7030C: 0x01EE, + 0x0292030C: 0x01EF, + 0x006A030C: 0x01F0, + 0x00470301: 0x01F4, + 0x00670301: 0x01F5, + 0x004E0300: 0x01F8, + 0x006E0300: 0x01F9, + 0x00C50301: 0x01FA, + 0x00E50301: 0x01FB, + 0x00C60301: 0x01FC, + 0x00E60301: 0x01FD, + 0x00D80301: 0x01FE, + 0x00F80301: 0x01FF, + 0x0041030F: 0x0200, + 0x0061030F: 0x0201, + 0x00410311: 0x0202, + 0x00610311: 0x0203, + 0x0045030F: 0x0204, + 0x0065030F: 0x0205, + 0x00450311: 0x0206, + 0x00650311: 0x0207, + 0x0049030F: 0x0208, + 0x0069030F: 0x0209, + 0x00490311: 0x020A, + 0x00690311: 0x020B, + 0x004F030F: 0x020C, + 0x006F030F: 0x020D, + 0x004F0311: 0x020E, + 0x006F0311: 0x020F, + 0x0052030F: 0x0210, + 0x0072030F: 0x0211, + 0x00520311: 0x0212, + 0x00720311: 0x0213, + 0x0055030F: 0x0214, + 0x0075030F: 0x0215, + 0x00550311: 0x0216, + 0x00750311: 0x0217, + 0x00530326: 0x0218, + 0x00730326: 0x0219, + 0x00540326: 0x021A, + 0x00740326: 0x021B, + 0x0048030C: 0x021E, + 0x0068030C: 0x021F, + 0x00410307: 0x0226, + 0x00610307: 0x0227, + 0x00450327: 0x0228, + 0x00650327: 0x0229, + 0x00D60304: 0x022A, + 0x00F60304: 0x022B, + 0x00D50304: 0x022C, + 0x00F50304: 0x022D, + 0x004F0307: 0x022E, + 0x006F0307: 0x022F, + 0x022E0304: 0x0230, + 0x022F0304: 0x0231, + 0x00590304: 0x0232, + 0x00790304: 0x0233, + 0x00A80301: 0x0385, + 0x03910301: 0x0386, + 0x03950301: 0x0388, + 0x03970301: 0x0389, + 0x03990301: 0x038A, + 0x039F0301: 0x038C, + 0x03A50301: 0x038E, + 0x03A90301: 0x038F, + 0x03CA0301: 0x0390, + 0x03990308: 0x03AA, + 0x03A50308: 0x03AB, + 0x03B10301: 0x03AC, + 0x03B50301: 0x03AD, + 0x03B70301: 0x03AE, + 0x03B90301: 0x03AF, + 0x03CB0301: 0x03B0, + 0x03B90308: 0x03CA, + 0x03C50308: 0x03CB, + 0x03BF0301: 0x03CC, + 0x03C50301: 0x03CD, + 0x03C90301: 0x03CE, + 0x03D20301: 0x03D3, + 0x03D20308: 0x03D4, + 0x04150300: 0x0400, + 0x04150308: 0x0401, + 0x04130301: 0x0403, + 0x04060308: 0x0407, + 0x041A0301: 0x040C, + 0x04180300: 0x040D, + 0x04230306: 0x040E, + 0x04180306: 0x0419, + 0x04380306: 0x0439, + 0x04350300: 0x0450, + 0x04350308: 0x0451, + 0x04330301: 0x0453, + 0x04560308: 0x0457, + 0x043A0301: 0x045C, + 0x04380300: 0x045D, + 0x04430306: 0x045E, + 0x0474030F: 0x0476, + 0x0475030F: 0x0477, + 0x04160306: 0x04C1, + 0x04360306: 0x04C2, + 0x04100306: 0x04D0, + 0x04300306: 0x04D1, + 0x04100308: 0x04D2, + 0x04300308: 0x04D3, + 0x04150306: 0x04D6, + 0x04350306: 0x04D7, + 0x04D80308: 0x04DA, + 0x04D90308: 0x04DB, + 0x04160308: 0x04DC, + 0x04360308: 0x04DD, + 0x04170308: 0x04DE, + 0x04370308: 0x04DF, + 0x04180304: 0x04E2, + 0x04380304: 0x04E3, + 0x04180308: 0x04E4, + 0x04380308: 0x04E5, + 0x041E0308: 0x04E6, + 0x043E0308: 0x04E7, + 0x04E80308: 0x04EA, + 0x04E90308: 0x04EB, + 0x042D0308: 0x04EC, + 0x044D0308: 0x04ED, + 0x04230304: 0x04EE, + 0x04430304: 0x04EF, + 0x04230308: 0x04F0, + 0x04430308: 0x04F1, + 0x0423030B: 0x04F2, + 0x0443030B: 0x04F3, + 0x04270308: 0x04F4, + 0x04470308: 0x04F5, + 0x042B0308: 0x04F8, + 0x044B0308: 0x04F9, + 0x06270653: 0x0622, + 0x06270654: 0x0623, + 0x06480654: 0x0624, + 0x06270655: 0x0625, + 0x064A0654: 0x0626, + 0x06D50654: 0x06C0, + 0x06C10654: 0x06C2, + 0x06D20654: 0x06D3, + 0x0928093C: 0x0929, + 0x0930093C: 0x0931, + 0x0933093C: 0x0934, + 0x09C709BE: 0x09CB, + 0x09C709D7: 0x09CC, + 0x0B470B56: 0x0B48, + 0x0B470B3E: 0x0B4B, + 0x0B470B57: 0x0B4C, + 0x0B920BD7: 0x0B94, + 0x0BC60BBE: 0x0BCA, + 0x0BC70BBE: 0x0BCB, + 0x0BC60BD7: 0x0BCC, + 0x0C460C56: 0x0C48, + 0x0CBF0CD5: 0x0CC0, + 0x0CC60CD5: 0x0CC7, + 0x0CC60CD6: 0x0CC8, + 0x0CC60CC2: 0x0CCA, + 0x0CCA0CD5: 0x0CCB, + 0x0D460D3E: 0x0D4A, + 0x0D470D3E: 0x0D4B, + 0x0D460D57: 0x0D4C, + 0x0DD90DCA: 0x0DDA, + 0x0DD90DCF: 0x0DDC, + 0x0DDC0DCA: 0x0DDD, + 0x0DD90DDF: 0x0DDE, + 0x1025102E: 0x1026, + 0x1B051B35: 0x1B06, + 0x1B071B35: 0x1B08, + 0x1B091B35: 0x1B0A, + 0x1B0B1B35: 0x1B0C, + 0x1B0D1B35: 0x1B0E, + 0x1B111B35: 0x1B12, + 0x1B3A1B35: 0x1B3B, + 0x1B3C1B35: 0x1B3D, + 0x1B3E1B35: 0x1B40, + 0x1B3F1B35: 0x1B41, + 0x1B421B35: 0x1B43, + 0x00410325: 0x1E00, + 0x00610325: 0x1E01, + 0x00420307: 0x1E02, + 0x00620307: 0x1E03, + 0x00420323: 0x1E04, + 0x00620323: 0x1E05, + 0x00420331: 0x1E06, + 0x00620331: 0x1E07, + 0x00C70301: 0x1E08, + 0x00E70301: 0x1E09, + 0x00440307: 0x1E0A, + 0x00640307: 0x1E0B, + 0x00440323: 0x1E0C, + 0x00640323: 0x1E0D, + 0x00440331: 0x1E0E, + 0x00640331: 0x1E0F, + 0x00440327: 0x1E10, + 0x00640327: 0x1E11, + 0x0044032D: 0x1E12, + 0x0064032D: 0x1E13, + 0x01120300: 0x1E14, + 0x01130300: 0x1E15, + 0x01120301: 0x1E16, + 0x01130301: 0x1E17, + 0x0045032D: 0x1E18, + 0x0065032D: 0x1E19, + 0x00450330: 0x1E1A, + 0x00650330: 0x1E1B, + 0x02280306: 0x1E1C, + 0x02290306: 0x1E1D, + 0x00460307: 0x1E1E, + 0x00660307: 0x1E1F, + 0x00470304: 0x1E20, + 0x00670304: 0x1E21, + 0x00480307: 0x1E22, + 0x00680307: 0x1E23, + 0x00480323: 0x1E24, + 0x00680323: 0x1E25, + 0x00480308: 0x1E26, + 0x00680308: 0x1E27, + 0x00480327: 0x1E28, + 0x00680327: 0x1E29, + 0x0048032E: 0x1E2A, + 0x0068032E: 0x1E2B, + 0x00490330: 0x1E2C, + 0x00690330: 0x1E2D, + 0x00CF0301: 0x1E2E, + 0x00EF0301: 0x1E2F, + 0x004B0301: 0x1E30, + 0x006B0301: 0x1E31, + 0x004B0323: 0x1E32, + 0x006B0323: 0x1E33, + 0x004B0331: 0x1E34, + 0x006B0331: 0x1E35, + 0x004C0323: 0x1E36, + 0x006C0323: 0x1E37, + 0x1E360304: 0x1E38, + 0x1E370304: 0x1E39, + 0x004C0331: 0x1E3A, + 0x006C0331: 0x1E3B, + 0x004C032D: 0x1E3C, + 0x006C032D: 0x1E3D, + 0x004D0301: 0x1E3E, + 0x006D0301: 0x1E3F, + 0x004D0307: 0x1E40, + 0x006D0307: 0x1E41, + 0x004D0323: 0x1E42, + 0x006D0323: 0x1E43, + 0x004E0307: 0x1E44, + 0x006E0307: 0x1E45, + 0x004E0323: 0x1E46, + 0x006E0323: 0x1E47, + 0x004E0331: 0x1E48, + 0x006E0331: 0x1E49, + 0x004E032D: 0x1E4A, + 0x006E032D: 0x1E4B, + 0x00D50301: 0x1E4C, + 0x00F50301: 0x1E4D, + 0x00D50308: 0x1E4E, + 0x00F50308: 0x1E4F, + 0x014C0300: 0x1E50, + 0x014D0300: 0x1E51, + 0x014C0301: 0x1E52, + 0x014D0301: 0x1E53, + 0x00500301: 0x1E54, + 0x00700301: 0x1E55, + 0x00500307: 0x1E56, + 0x00700307: 0x1E57, + 0x00520307: 0x1E58, + 0x00720307: 0x1E59, + 0x00520323: 0x1E5A, + 0x00720323: 0x1E5B, + 0x1E5A0304: 0x1E5C, + 0x1E5B0304: 0x1E5D, + 0x00520331: 0x1E5E, + 0x00720331: 0x1E5F, + 0x00530307: 0x1E60, + 0x00730307: 0x1E61, + 0x00530323: 0x1E62, + 0x00730323: 0x1E63, + 0x015A0307: 0x1E64, + 0x015B0307: 0x1E65, + 0x01600307: 0x1E66, + 0x01610307: 0x1E67, + 0x1E620307: 0x1E68, + 0x1E630307: 0x1E69, + 0x00540307: 0x1E6A, + 0x00740307: 0x1E6B, + 0x00540323: 0x1E6C, + 0x00740323: 0x1E6D, + 0x00540331: 0x1E6E, + 0x00740331: 0x1E6F, + 0x0054032D: 0x1E70, + 0x0074032D: 0x1E71, + 0x00550324: 0x1E72, + 0x00750324: 0x1E73, + 0x00550330: 0x1E74, + 0x00750330: 0x1E75, + 0x0055032D: 0x1E76, + 0x0075032D: 0x1E77, + 0x01680301: 0x1E78, + 0x01690301: 0x1E79, + 0x016A0308: 0x1E7A, + 0x016B0308: 0x1E7B, + 0x00560303: 0x1E7C, + 0x00760303: 0x1E7D, + 0x00560323: 0x1E7E, + 0x00760323: 0x1E7F, + 0x00570300: 0x1E80, + 0x00770300: 0x1E81, + 0x00570301: 0x1E82, + 0x00770301: 0x1E83, + 0x00570308: 0x1E84, + 0x00770308: 0x1E85, + 0x00570307: 0x1E86, + 0x00770307: 0x1E87, + 0x00570323: 0x1E88, + 0x00770323: 0x1E89, + 0x00580307: 0x1E8A, + 0x00780307: 0x1E8B, + 0x00580308: 0x1E8C, + 0x00780308: 0x1E8D, + 0x00590307: 0x1E8E, + 0x00790307: 0x1E8F, + 0x005A0302: 0x1E90, + 0x007A0302: 0x1E91, + 0x005A0323: 0x1E92, + 0x007A0323: 0x1E93, + 0x005A0331: 0x1E94, + 0x007A0331: 0x1E95, + 0x00680331: 0x1E96, + 0x00740308: 0x1E97, + 0x0077030A: 0x1E98, + 0x0079030A: 0x1E99, + 0x017F0307: 0x1E9B, + 0x00410323: 0x1EA0, + 0x00610323: 0x1EA1, + 0x00410309: 0x1EA2, + 0x00610309: 0x1EA3, + 0x00C20301: 0x1EA4, + 0x00E20301: 0x1EA5, + 0x00C20300: 0x1EA6, + 0x00E20300: 0x1EA7, + 0x00C20309: 0x1EA8, + 0x00E20309: 0x1EA9, + 0x00C20303: 0x1EAA, + 0x00E20303: 0x1EAB, + 0x1EA00302: 0x1EAC, + 0x1EA10302: 0x1EAD, + 0x01020301: 0x1EAE, + 0x01030301: 0x1EAF, + 0x01020300: 0x1EB0, + 0x01030300: 0x1EB1, + 0x01020309: 0x1EB2, + 0x01030309: 0x1EB3, + 0x01020303: 0x1EB4, + 0x01030303: 0x1EB5, + 0x1EA00306: 0x1EB6, + 0x1EA10306: 0x1EB7, + 0x00450323: 0x1EB8, + 0x00650323: 0x1EB9, + 0x00450309: 0x1EBA, + 0x00650309: 0x1EBB, + 0x00450303: 0x1EBC, + 0x00650303: 0x1EBD, + 0x00CA0301: 0x1EBE, + 0x00EA0301: 0x1EBF, + 0x00CA0300: 0x1EC0, + 0x00EA0300: 0x1EC1, + 0x00CA0309: 0x1EC2, + 0x00EA0309: 0x1EC3, + 0x00CA0303: 0x1EC4, + 0x00EA0303: 0x1EC5, + 0x1EB80302: 0x1EC6, + 0x1EB90302: 0x1EC7, + 0x00490309: 0x1EC8, + 0x00690309: 0x1EC9, + 0x00490323: 0x1ECA, + 0x00690323: 0x1ECB, + 0x004F0323: 0x1ECC, + 0x006F0323: 0x1ECD, + 0x004F0309: 0x1ECE, + 0x006F0309: 0x1ECF, + 0x00D40301: 0x1ED0, + 0x00F40301: 0x1ED1, + 0x00D40300: 0x1ED2, + 0x00F40300: 0x1ED3, + 0x00D40309: 0x1ED4, + 0x00F40309: 0x1ED5, + 0x00D40303: 0x1ED6, + 0x00F40303: 0x1ED7, + 0x1ECC0302: 0x1ED8, + 0x1ECD0302: 0x1ED9, + 0x01A00301: 0x1EDA, + 0x01A10301: 0x1EDB, + 0x01A00300: 0x1EDC, + 0x01A10300: 0x1EDD, + 0x01A00309: 0x1EDE, + 0x01A10309: 0x1EDF, + 0x01A00303: 0x1EE0, + 0x01A10303: 0x1EE1, + 0x01A00323: 0x1EE2, + 0x01A10323: 0x1EE3, + 0x00550323: 0x1EE4, + 0x00750323: 0x1EE5, + 0x00550309: 0x1EE6, + 0x00750309: 0x1EE7, + 0x01AF0301: 0x1EE8, + 0x01B00301: 0x1EE9, + 0x01AF0300: 0x1EEA, + 0x01B00300: 0x1EEB, + 0x01AF0309: 0x1EEC, + 0x01B00309: 0x1EED, + 0x01AF0303: 0x1EEE, + 0x01B00303: 0x1EEF, + 0x01AF0323: 0x1EF0, + 0x01B00323: 0x1EF1, + 0x00590300: 0x1EF2, + 0x00790300: 0x1EF3, + 0x00590323: 0x1EF4, + 0x00790323: 0x1EF5, + 0x00590309: 0x1EF6, + 0x00790309: 0x1EF7, + 0x00590303: 0x1EF8, + 0x00790303: 0x1EF9, + 0x03B10313: 0x1F00, + 0x03B10314: 0x1F01, + 0x1F000300: 0x1F02, + 0x1F010300: 0x1F03, + 0x1F000301: 0x1F04, + 0x1F010301: 0x1F05, + 0x1F000342: 0x1F06, + 0x1F010342: 0x1F07, + 0x03910313: 0x1F08, + 0x03910314: 0x1F09, + 0x1F080300: 0x1F0A, + 0x1F090300: 0x1F0B, + 0x1F080301: 0x1F0C, + 0x1F090301: 0x1F0D, + 0x1F080342: 0x1F0E, + 0x1F090342: 0x1F0F, + 0x03B50313: 0x1F10, + 0x03B50314: 0x1F11, + 0x1F100300: 0x1F12, + 0x1F110300: 0x1F13, + 0x1F100301: 0x1F14, + 0x1F110301: 0x1F15, + 0x03950313: 0x1F18, + 0x03950314: 0x1F19, + 0x1F180300: 0x1F1A, + 0x1F190300: 0x1F1B, + 0x1F180301: 0x1F1C, + 0x1F190301: 0x1F1D, + 0x03B70313: 0x1F20, + 0x03B70314: 0x1F21, + 0x1F200300: 0x1F22, + 0x1F210300: 0x1F23, + 0x1F200301: 0x1F24, + 0x1F210301: 0x1F25, + 0x1F200342: 0x1F26, + 0x1F210342: 0x1F27, + 0x03970313: 0x1F28, + 0x03970314: 0x1F29, + 0x1F280300: 0x1F2A, + 0x1F290300: 0x1F2B, + 0x1F280301: 0x1F2C, + 0x1F290301: 0x1F2D, + 0x1F280342: 0x1F2E, + 0x1F290342: 0x1F2F, + 0x03B90313: 0x1F30, + 0x03B90314: 0x1F31, + 0x1F300300: 0x1F32, + 0x1F310300: 0x1F33, + 0x1F300301: 0x1F34, + 0x1F310301: 0x1F35, + 0x1F300342: 0x1F36, + 0x1F310342: 0x1F37, + 0x03990313: 0x1F38, + 0x03990314: 0x1F39, + 0x1F380300: 0x1F3A, + 0x1F390300: 0x1F3B, + 0x1F380301: 0x1F3C, + 0x1F390301: 0x1F3D, + 0x1F380342: 0x1F3E, + 0x1F390342: 0x1F3F, + 0x03BF0313: 0x1F40, + 0x03BF0314: 0x1F41, + 0x1F400300: 0x1F42, + 0x1F410300: 0x1F43, + 0x1F400301: 0x1F44, + 0x1F410301: 0x1F45, + 0x039F0313: 0x1F48, + 0x039F0314: 0x1F49, + 0x1F480300: 0x1F4A, + 0x1F490300: 0x1F4B, + 0x1F480301: 0x1F4C, + 0x1F490301: 0x1F4D, + 0x03C50313: 0x1F50, + 0x03C50314: 0x1F51, + 0x1F500300: 0x1F52, + 0x1F510300: 0x1F53, + 0x1F500301: 0x1F54, + 0x1F510301: 0x1F55, + 0x1F500342: 0x1F56, + 0x1F510342: 0x1F57, + 0x03A50314: 0x1F59, + 0x1F590300: 0x1F5B, + 0x1F590301: 0x1F5D, + 0x1F590342: 0x1F5F, + 0x03C90313: 0x1F60, + 0x03C90314: 0x1F61, + 0x1F600300: 0x1F62, + 0x1F610300: 0x1F63, + 0x1F600301: 0x1F64, + 0x1F610301: 0x1F65, + 0x1F600342: 0x1F66, + 0x1F610342: 0x1F67, + 0x03A90313: 0x1F68, + 0x03A90314: 0x1F69, + 0x1F680300: 0x1F6A, + 0x1F690300: 0x1F6B, + 0x1F680301: 0x1F6C, + 0x1F690301: 0x1F6D, + 0x1F680342: 0x1F6E, + 0x1F690342: 0x1F6F, + 0x03B10300: 0x1F70, + 0x03B50300: 0x1F72, + 0x03B70300: 0x1F74, + 0x03B90300: 0x1F76, + 0x03BF0300: 0x1F78, + 0x03C50300: 0x1F7A, + 0x03C90300: 0x1F7C, + 0x1F000345: 0x1F80, + 0x1F010345: 0x1F81, + 0x1F020345: 0x1F82, + 0x1F030345: 0x1F83, + 0x1F040345: 0x1F84, + 0x1F050345: 0x1F85, + 0x1F060345: 0x1F86, + 0x1F070345: 0x1F87, + 0x1F080345: 0x1F88, + 0x1F090345: 0x1F89, + 0x1F0A0345: 0x1F8A, + 0x1F0B0345: 0x1F8B, + 0x1F0C0345: 0x1F8C, + 0x1F0D0345: 0x1F8D, + 0x1F0E0345: 0x1F8E, + 0x1F0F0345: 0x1F8F, + 0x1F200345: 0x1F90, + 0x1F210345: 0x1F91, + 0x1F220345: 0x1F92, + 0x1F230345: 0x1F93, + 0x1F240345: 0x1F94, + 0x1F250345: 0x1F95, + 0x1F260345: 0x1F96, + 0x1F270345: 0x1F97, + 0x1F280345: 0x1F98, + 0x1F290345: 0x1F99, + 0x1F2A0345: 0x1F9A, + 0x1F2B0345: 0x1F9B, + 0x1F2C0345: 0x1F9C, + 0x1F2D0345: 0x1F9D, + 0x1F2E0345: 0x1F9E, + 0x1F2F0345: 0x1F9F, + 0x1F600345: 0x1FA0, + 0x1F610345: 0x1FA1, + 0x1F620345: 0x1FA2, + 0x1F630345: 0x1FA3, + 0x1F640345: 0x1FA4, + 0x1F650345: 0x1FA5, + 0x1F660345: 0x1FA6, + 0x1F670345: 0x1FA7, + 0x1F680345: 0x1FA8, + 0x1F690345: 0x1FA9, + 0x1F6A0345: 0x1FAA, + 0x1F6B0345: 0x1FAB, + 0x1F6C0345: 0x1FAC, + 0x1F6D0345: 0x1FAD, + 0x1F6E0345: 0x1FAE, + 0x1F6F0345: 0x1FAF, + 0x03B10306: 0x1FB0, + 0x03B10304: 0x1FB1, + 0x1F700345: 0x1FB2, + 0x03B10345: 0x1FB3, + 0x03AC0345: 0x1FB4, + 0x03B10342: 0x1FB6, + 0x1FB60345: 0x1FB7, + 0x03910306: 0x1FB8, + 0x03910304: 0x1FB9, + 0x03910300: 0x1FBA, + 0x03910345: 0x1FBC, + 0x00A80342: 0x1FC1, + 0x1F740345: 0x1FC2, + 0x03B70345: 0x1FC3, + 0x03AE0345: 0x1FC4, + 0x03B70342: 0x1FC6, + 0x1FC60345: 0x1FC7, + 0x03950300: 0x1FC8, + 0x03970300: 0x1FCA, + 0x03970345: 0x1FCC, + 0x1FBF0300: 0x1FCD, + 0x1FBF0301: 0x1FCE, + 0x1FBF0342: 0x1FCF, + 0x03B90306: 0x1FD0, + 0x03B90304: 0x1FD1, + 0x03CA0300: 0x1FD2, + 0x03B90342: 0x1FD6, + 0x03CA0342: 0x1FD7, + 0x03990306: 0x1FD8, + 0x03990304: 0x1FD9, + 0x03990300: 0x1FDA, + 0x1FFE0300: 0x1FDD, + 0x1FFE0301: 0x1FDE, + 0x1FFE0342: 0x1FDF, + 0x03C50306: 0x1FE0, + 0x03C50304: 0x1FE1, + 0x03CB0300: 0x1FE2, + 0x03C10313: 0x1FE4, + 0x03C10314: 0x1FE5, + 0x03C50342: 0x1FE6, + 0x03CB0342: 0x1FE7, + 0x03A50306: 0x1FE8, + 0x03A50304: 0x1FE9, + 0x03A50300: 0x1FEA, + 0x03A10314: 0x1FEC, + 0x00A80300: 0x1FED, + 0x1F7C0345: 0x1FF2, + 0x03C90345: 0x1FF3, + 0x03CE0345: 0x1FF4, + 0x03C90342: 0x1FF6, + 0x1FF60345: 0x1FF7, + 0x039F0300: 0x1FF8, + 0x03A90300: 0x1FFA, + 0x03A90345: 0x1FFC, + 0x21900338: 0x219A, + 0x21920338: 0x219B, + 0x21940338: 0x21AE, + 0x21D00338: 0x21CD, + 0x21D40338: 0x21CE, + 0x21D20338: 0x21CF, + 0x22030338: 0x2204, + 0x22080338: 0x2209, + 0x220B0338: 0x220C, + 0x22230338: 0x2224, + 0x22250338: 0x2226, + 0x223C0338: 0x2241, + 0x22430338: 0x2244, + 0x22450338: 0x2247, + 0x22480338: 0x2249, + 0x003D0338: 0x2260, + 0x22610338: 0x2262, + 0x224D0338: 0x226D, + 0x003C0338: 0x226E, + 0x003E0338: 0x226F, + 0x22640338: 0x2270, + 0x22650338: 0x2271, + 0x22720338: 0x2274, + 0x22730338: 0x2275, + 0x22760338: 0x2278, + 0x22770338: 0x2279, + 0x227A0338: 0x2280, + 0x227B0338: 0x2281, + 0x22820338: 0x2284, + 0x22830338: 0x2285, + 0x22860338: 0x2288, + 0x22870338: 0x2289, + 0x22A20338: 0x22AC, + 0x22A80338: 0x22AD, + 0x22A90338: 0x22AE, + 0x22AB0338: 0x22AF, + 0x227C0338: 0x22E0, + 0x227D0338: 0x22E1, + 0x22910338: 0x22E2, + 0x22920338: 0x22E3, + 0x22B20338: 0x22EA, + 0x22B30338: 0x22EB, + 0x22B40338: 0x22EC, + 0x22B50338: 0x22ED, + 0x304B3099: 0x304C, + 0x304D3099: 0x304E, + 0x304F3099: 0x3050, + 0x30513099: 0x3052, + 0x30533099: 0x3054, + 0x30553099: 0x3056, + 0x30573099: 0x3058, + 0x30593099: 0x305A, + 0x305B3099: 0x305C, + 0x305D3099: 0x305E, + 0x305F3099: 0x3060, + 0x30613099: 0x3062, + 0x30643099: 0x3065, + 0x30663099: 0x3067, + 0x30683099: 0x3069, + 0x306F3099: 0x3070, + 0x306F309A: 0x3071, + 0x30723099: 0x3073, + 0x3072309A: 0x3074, + 0x30753099: 0x3076, + 0x3075309A: 0x3077, + 0x30783099: 0x3079, + 0x3078309A: 0x307A, + 0x307B3099: 0x307C, + 0x307B309A: 0x307D, + 0x30463099: 0x3094, + 0x309D3099: 0x309E, + 0x30AB3099: 0x30AC, + 0x30AD3099: 0x30AE, + 0x30AF3099: 0x30B0, + 0x30B13099: 0x30B2, + 0x30B33099: 0x30B4, + 0x30B53099: 0x30B6, + 0x30B73099: 0x30B8, + 0x30B93099: 0x30BA, + 0x30BB3099: 0x30BC, + 0x30BD3099: 0x30BE, + 0x30BF3099: 0x30C0, + 0x30C13099: 0x30C2, + 0x30C43099: 0x30C5, + 0x30C63099: 0x30C7, + 0x30C83099: 0x30C9, + 0x30CF3099: 0x30D0, + 0x30CF309A: 0x30D1, + 0x30D23099: 0x30D3, + 0x30D2309A: 0x30D4, + 0x30D53099: 0x30D6, + 0x30D5309A: 0x30D7, + 0x30D83099: 0x30D9, + 0x30D8309A: 0x30DA, + 0x30DB3099: 0x30DC, + 0x30DB309A: 0x30DD, + 0x30A63099: 0x30F4, + 0x30EF3099: 0x30F7, + 0x30F03099: 0x30F8, + 0x30F13099: 0x30F9, + 0x30F23099: 0x30FA, + 0x30FD3099: 0x30FE, + 0x109910BA: 0x1109A, + 0x109B10BA: 0x1109C, + 0x10A510BA: 0x110AB, + 0x11311127: 0x1112E, + 0x11321127: 0x1112F, + 0x1347133E: 0x1134B, + 0x13471357: 0x1134C, + 0x14B914BA: 0x114BB, + 0x14B914B0: 0x114BC, + 0x14B914BD: 0x114BE, + 0x15B815AF: 0x115BA, + 0x15B915AF: 0x115BB, +} + +// Total size of tables: 53KB (53848 bytes) diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/transform.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/transform.go new file mode 100644 index 000000000..8589067cd --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/transform.go @@ -0,0 +1,88 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "unicode/utf8" + + "golang.org/x/text/transform" +) + +// Reset implements the Reset method of the transform.Transformer interface. +func (Form) Reset() {} + +// Transform implements the Transform method of the transform.Transformer +// interface. It may need to write segments of up to MaxSegmentSize at once. +// Users should either catch ErrShortDst and allow dst to grow or have dst be at +// least of size MaxTransformChunkSize to be guaranteed of progress. +func (f Form) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + n := 0 + // Cap the maximum number of src bytes to check. + b := src + eof := atEOF + if ns := len(dst); ns < len(b) { + err = transform.ErrShortDst + eof = false + b = b[:ns] + } + i, ok := formTable[f].quickSpan(inputBytes(b), n, len(b), eof) + n += copy(dst[n:], b[n:i]) + if !ok { + nDst, nSrc, err = f.transform(dst[n:], src[n:], atEOF) + return nDst + n, nSrc + n, err + } + if n < len(src) && !atEOF { + err = transform.ErrShortSrc + } + return n, n, err +} + +func flushTransform(rb *reorderBuffer) bool { + // Write out (must fully fit in dst, or else it is a ErrShortDst). + if len(rb.out) < rb.nrune*utf8.UTFMax { + return false + } + rb.out = rb.out[rb.flushCopy(rb.out):] + return true +} + +var errs = []error{nil, transform.ErrShortDst, transform.ErrShortSrc} + +// transform implements the transform.Transformer interface. It is only called +// when quickSpan does not pass for a given string. +func (f Form) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + // TODO: get rid of reorderBuffer. See CL 23460044. + rb := reorderBuffer{} + rb.init(f, src) + for { + // Load segment into reorder buffer. + rb.setFlusher(dst[nDst:], flushTransform) + end := decomposeSegment(&rb, nSrc, atEOF) + if end < 0 { + return nDst, nSrc, errs[-end] + } + nDst = len(dst) - len(rb.out) + nSrc = end + + // Next quickSpan. + end = rb.nsrc + eof := atEOF + if n := nSrc + len(dst) - nDst; n < end { + err = transform.ErrShortDst + end = n + eof = false + } + end, ok := rb.f.quickSpan(rb.src, nSrc, end, eof) + n := copy(dst[nDst:], rb.src.bytes[nSrc:end]) + nSrc += n + nDst += n + if ok { + if n < rb.nsrc && !atEOF { + err = transform.ErrShortSrc + } + return nDst, nSrc, err + } + } +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/transform_test.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/transform_test.go new file mode 100644 index 000000000..987d680ed --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/transform_test.go @@ -0,0 +1,101 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "fmt" + "testing" + + "golang.org/x/text/transform" +) + +func TestTransform(t *testing.T) { + tests := []struct { + f Form + in, out string + eof bool + dstSize int + err error + }{ + {NFC, "ab", "ab", true, 2, nil}, + {NFC, "qx", "qx", true, 2, nil}, + {NFD, "qx", "qx", true, 2, nil}, + {NFC, "", "", true, 1, nil}, + {NFD, "", "", true, 1, nil}, + {NFC, "", "", false, 1, nil}, + {NFD, "", "", false, 1, nil}, + + // Normalized segment does not fit in destination. + {NFD, "ö", "", true, 1, transform.ErrShortDst}, + {NFD, "ö", "", true, 2, transform.ErrShortDst}, + + // As an artifact of the algorithm, only full segments are written. + // This is not strictly required, and some bytes could be written. + // In practice, for Transform to not block, the destination buffer + // should be at least MaxSegmentSize to work anyway and these edge + // conditions will be relatively rare. + {NFC, "ab", "", true, 1, transform.ErrShortDst}, + // This is even true for inert runes. + {NFC, "qx", "", true, 1, transform.ErrShortDst}, + {NFC, "a\u0300abc", "\u00e0a", true, 4, transform.ErrShortDst}, + + // We cannot write a segment if succesive runes could still change the result. + {NFD, "ö", "", false, 3, transform.ErrShortSrc}, + {NFC, "a\u0300", "", false, 4, transform.ErrShortSrc}, + {NFD, "a\u0300", "", false, 4, transform.ErrShortSrc}, + {NFC, "ö", "", false, 3, transform.ErrShortSrc}, + + {NFC, "a\u0300", "", true, 1, transform.ErrShortDst}, + // Theoretically could fit, but won't due to simplified checks. + {NFC, "a\u0300", "", true, 2, transform.ErrShortDst}, + {NFC, "a\u0300", "", true, 3, transform.ErrShortDst}, + {NFC, "a\u0300", "\u00e0", true, 4, nil}, + + {NFD, "öa\u0300", "o\u0308", false, 8, transform.ErrShortSrc}, + {NFD, "öa\u0300ö", "o\u0308a\u0300", true, 8, transform.ErrShortDst}, + {NFD, "öa\u0300ö", "o\u0308a\u0300", false, 12, transform.ErrShortSrc}, + + // Illegal input is copied verbatim. + {NFD, "\xbd\xb2=\xbc ", "\xbd\xb2=\xbc ", true, 8, nil}, + } + b := make([]byte, 100) + for i, tt := range tests { + nDst, _, err := tt.f.Transform(b[:tt.dstSize], []byte(tt.in), tt.eof) + out := string(b[:nDst]) + if out != tt.out || err != tt.err { + t.Errorf("%d: was %+q (%v); want %+q (%v)", i, out, err, tt.out, tt.err) + } + if want := tt.f.String(tt.in)[:nDst]; want != out { + t.Errorf("%d: incorect normalization: was %+q; want %+q", i, out, want) + } + } +} + +var transBufSizes = []int{ + MaxTransformChunkSize, + 3 * MaxTransformChunkSize / 2, + 2 * MaxTransformChunkSize, + 3 * MaxTransformChunkSize, + 100 * MaxTransformChunkSize, +} + +func doTransNorm(f Form, buf []byte, b []byte) []byte { + acc := []byte{} + for p := 0; p < len(b); { + nd, ns, _ := f.Transform(buf[:], b[p:], true) + p += ns + acc = append(acc, buf[:nd]...) + } + return acc +} + +func TestTransformNorm(t *testing.T) { + for _, sz := range transBufSizes { + buf := make([]byte, sz) + runNormTests(t, fmt.Sprintf("Transform:%d", sz), func(f Form, out []byte, s string) []byte { + return doTransNorm(f, buf, append(out, s...)) + }) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/trie.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/trie.go new file mode 100644 index 000000000..423386bf4 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/trie.go @@ -0,0 +1,54 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +type valueRange struct { + value uint16 // header: value:stride + lo, hi byte // header: lo:n +} + +type sparseBlocks struct { + values []valueRange + offset []uint16 +} + +var nfcSparse = sparseBlocks{ + values: nfcSparseValues[:], + offset: nfcSparseOffset[:], +} + +var nfkcSparse = sparseBlocks{ + values: nfkcSparseValues[:], + offset: nfkcSparseOffset[:], +} + +var ( + nfcData = newNfcTrie(0) + nfkcData = newNfkcTrie(0) +) + +// lookupValue determines the type of block n and looks up the value for b. +// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block +// is a list of ranges with an accompanying value. Given a matching range r, +// the value for b is by r.value + (b - r.lo) * stride. +func (t *sparseBlocks) lookup(n uint32, b byte) uint16 { + offset := t.offset[n] + header := t.values[offset] + lo := offset + 1 + hi := lo + uint16(header.lo) + for lo < hi { + m := lo + (hi-lo)/2 + r := t.values[m] + if r.lo <= b && b <= r.hi { + return r.value + uint16(b-r.lo)*header.value + } + if b < r.lo { + hi = m + } else { + lo = m + 1 + } + } + return 0 +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/triegen.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/triegen.go new file mode 100644 index 000000000..45d711900 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/triegen.go @@ -0,0 +1,117 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Trie table generator. +// Used by make*tables tools to generate a go file with trie data structures +// for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte +// sequence are used to lookup offsets in the index table to be used for the +// next byte. The last byte is used to index into a table with 16-bit values. + +package main + +import ( + "fmt" + "io" +) + +const maxSparseEntries = 16 + +type normCompacter struct { + sparseBlocks [][]uint64 + sparseOffset []uint16 + sparseCount int + name string +} + +func mostFrequentStride(a []uint64) int { + counts := make(map[int]int) + var v int + for _, x := range a { + if stride := int(x) - v; v != 0 && stride >= 0 { + counts[stride]++ + } + v = int(x) + } + var maxs, maxc int + for stride, cnt := range counts { + if cnt > maxc || (cnt == maxc && stride < maxs) { + maxs, maxc = stride, cnt + } + } + return maxs +} + +func countSparseEntries(a []uint64) int { + stride := mostFrequentStride(a) + var v, count int + for _, tv := range a { + if int(tv)-v != stride { + if tv != 0 { + count++ + } + } + v = int(tv) + } + return count +} + +func (c *normCompacter) Size(v []uint64) (sz int, ok bool) { + if n := countSparseEntries(v); n <= maxSparseEntries { + return (n+1)*4 + 2, true + } + return 0, false +} + +func (c *normCompacter) Store(v []uint64) uint32 { + h := uint32(len(c.sparseOffset)) + c.sparseBlocks = append(c.sparseBlocks, v) + c.sparseOffset = append(c.sparseOffset, uint16(c.sparseCount)) + c.sparseCount += countSparseEntries(v) + 1 + return h +} + +func (c *normCompacter) Handler() string { + return c.name + "Sparse.lookup" +} + +func (c *normCompacter) Print(w io.Writer) (retErr error) { + p := func(f string, x ...interface{}) { + if _, err := fmt.Fprintf(w, f, x...); retErr == nil && err != nil { + retErr = err + } + } + + ls := len(c.sparseBlocks) + p("// %sSparseOffset: %d entries, %d bytes\n", c.name, ls, ls*2) + p("var %sSparseOffset = %#v\n\n", c.name, c.sparseOffset) + + ns := c.sparseCount + p("// %sSparseValues: %d entries, %d bytes\n", c.name, ns, ns*4) + p("var %sSparseValues = [%d]valueRange {", c.name, ns) + for i, b := range c.sparseBlocks { + p("\n// Block %#x, offset %#x", i, c.sparseOffset[i]) + var v int + stride := mostFrequentStride(b) + n := countSparseEntries(b) + p("\n{value:%#04x,lo:%#02x},", stride, uint8(n)) + for i, nv := range b { + if int(nv)-v != stride { + if v != 0 { + p(",hi:%#02x},", 0x80+i-1) + } + if nv != 0 { + p("\n{value:%#04x,lo:%#02x", nv, 0x80+i) + } + } + v = int(nv) + } + if v != 0 { + p(",hi:%#02x},", 0x80+len(b)-1) + } + } + p("\n}\n\n") + return +} diff --git a/Godeps/_workspace/src/golang.org/x/text/unicode/norm/ucd_test.go b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/ucd_test.go new file mode 100644 index 000000000..a949a298a --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/text/unicode/norm/ucd_test.go @@ -0,0 +1,279 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "regexp" + "runtime" + "strconv" + "strings" + "sync" + "testing" + "time" + "unicode/utf8" + + "golang.org/x/text/internal/gen" +) + +var long = flag.Bool("long", false, + "run time-consuming tests, such as tests that fetch data online") + +var once sync.Once + +func skipShort(t *testing.T) { + if !gen.IsLocal() && !*long { + t.Skip("skipping test to prevent downloading; to run use -long or use -local to specify a local source") + } + once.Do(func() { loadTestData(t) }) +} + +// This regression test runs the test set in NormalizationTest.txt +// (taken from http://www.unicode.org/Public//ucd/). +// +// NormalizationTest.txt has form: +// @Part0 # Specific cases +// # +// 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE +// 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW +// +// Each test has 5 columns (c1, c2, c3, c4, c5), where +// (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1)) +// +// CONFORMANCE: +// 1. The following invariants must be true for all conformant implementations +// +// NFC +// c2 == NFC(c1) == NFC(c2) == NFC(c3) +// c4 == NFC(c4) == NFC(c5) +// +// NFD +// c3 == NFD(c1) == NFD(c2) == NFD(c3) +// c5 == NFD(c4) == NFD(c5) +// +// NFKC +// c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5) +// +// NFKD +// c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5) +// +// 2. For every code point X assigned in this version of Unicode that is not +// specifically listed in Part 1, the following invariants must be true +// for all conformant implementations: +// +// X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X) +// + +// Column types. +const ( + cRaw = iota + cNFC + cNFD + cNFKC + cNFKD + cMaxColumns +) + +// Holds data from NormalizationTest.txt +var part []Part + +type Part struct { + name string + number int + tests []Test +} + +type Test struct { + name string + partnr int + number int + r rune // used for character by character test + cols [cMaxColumns]string // Each has 5 entries, see below. +} + +func (t Test) Name() string { + if t.number < 0 { + return part[t.partnr].name + } + return fmt.Sprintf("%s:%d", part[t.partnr].name, t.number) +} + +var partRe = regexp.MustCompile(`@Part(\d) # (.*)$`) +var testRe = regexp.MustCompile(`^` + strings.Repeat(`([\dA-F ]+);`, 5) + ` # (.*)$`) + +var counter int + +// Load the data form NormalizationTest.txt +func loadTestData(t *testing.T) { + f := gen.OpenUCDFile("NormalizationTest.txt") + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + if len(line) == 0 || line[0] == '#' { + continue + } + m := partRe.FindStringSubmatch(line) + if m != nil { + if len(m) < 3 { + t.Fatal("Failed to parse Part: ", line) + } + i, err := strconv.Atoi(m[1]) + if err != nil { + t.Fatal(err) + } + name := m[2] + part = append(part, Part{name: name[:len(name)-1], number: i}) + continue + } + m = testRe.FindStringSubmatch(line) + if m == nil || len(m) < 7 { + t.Fatalf(`Failed to parse: "%s" result: %#v`, line, m) + } + test := Test{name: m[6], partnr: len(part) - 1, number: counter} + counter++ + for j := 1; j < len(m)-1; j++ { + for _, split := range strings.Split(m[j], " ") { + r, err := strconv.ParseUint(split, 16, 64) + if err != nil { + t.Fatal(err) + } + if test.r == 0 { + // save for CharacterByCharacterTests + test.r = rune(r) + } + var buf [utf8.UTFMax]byte + sz := utf8.EncodeRune(buf[:], rune(r)) + test.cols[j-1] += string(buf[:sz]) + } + } + part := &part[len(part)-1] + part.tests = append(part.tests, test) + } + if scanner.Err() != nil { + t.Fatal(scanner.Err()) + } +} + +func cmpResult(t *testing.T, tc *Test, name string, f Form, gold, test, result string) { + if gold != result { + t.Errorf("%s:%s: %s(%+q)=%+q; want %+q: %s", + tc.Name(), name, fstr[f], test, result, gold, tc.name) + } +} + +func cmpIsNormal(t *testing.T, tc *Test, name string, f Form, test string, result, want bool) { + if result != want { + t.Errorf("%s:%s: %s(%+q)=%v; want %v", tc.Name(), name, fstr[f], test, result, want) + } +} + +func doTest(t *testing.T, tc *Test, f Form, gold, test string) { + testb := []byte(test) + result := f.Bytes(testb) + cmpResult(t, tc, "Bytes", f, gold, test, string(result)) + + sresult := f.String(test) + cmpResult(t, tc, "String", f, gold, test, sresult) + + acc := []byte{} + i := Iter{} + i.InitString(f, test) + for !i.Done() { + acc = append(acc, i.Next()...) + } + cmpResult(t, tc, "Iter.Next", f, gold, test, string(acc)) + + buf := make([]byte, 128) + acc = nil + for p := 0; p < len(testb); { + nDst, nSrc, _ := f.Transform(buf, testb[p:], true) + acc = append(acc, buf[:nDst]...) + p += nSrc + } + cmpResult(t, tc, "Transform", f, gold, test, string(acc)) + + for i := range test { + out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...) + cmpResult(t, tc, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out)) + } + cmpIsNormal(t, tc, "IsNormal", f, test, f.IsNormal([]byte(test)), test == gold) + cmpIsNormal(t, tc, "IsNormalString", f, test, f.IsNormalString(test), test == gold) +} + +func doConformanceTests(t *testing.T, tc *Test, partn int) { + for i := 0; i <= 2; i++ { + doTest(t, tc, NFC, tc.cols[1], tc.cols[i]) + doTest(t, tc, NFD, tc.cols[2], tc.cols[i]) + doTest(t, tc, NFKC, tc.cols[3], tc.cols[i]) + doTest(t, tc, NFKD, tc.cols[4], tc.cols[i]) + } + for i := 3; i <= 4; i++ { + doTest(t, tc, NFC, tc.cols[3], tc.cols[i]) + doTest(t, tc, NFD, tc.cols[4], tc.cols[i]) + doTest(t, tc, NFKC, tc.cols[3], tc.cols[i]) + doTest(t, tc, NFKD, tc.cols[4], tc.cols[i]) + } +} + +func TestCharacterByCharacter(t *testing.T) { + skipShort(t) + tests := part[1].tests + var last rune = 0 + for i := 0; i <= len(tests); i++ { // last one is special case + var r rune + if i == len(tests) { + r = 0x2FA1E // Don't have to go to 0x10FFFF + } else { + r = tests[i].r + } + for last++; last < r; last++ { + // Check all characters that were not explicitly listed in the test. + tc := &Test{partnr: 1, number: -1} + char := string(last) + doTest(t, tc, NFC, char, char) + doTest(t, tc, NFD, char, char) + doTest(t, tc, NFKC, char, char) + doTest(t, tc, NFKD, char, char) + } + if i < len(tests) { + doConformanceTests(t, &tests[i], 1) + } + } +} + +func TestStandardTests(t *testing.T) { + skipShort(t) + for _, j := range []int{0, 2, 3} { + for _, test := range part[j].tests { + doConformanceTests(t, &test, j) + } + } +} + +// TestPerformance verifies that normalization is O(n). If any of the +// code does not properly check for maxCombiningChars, normalization +// may exhibit O(n**2) behavior. +func TestPerformance(t *testing.T) { + skipShort(t) + runtime.GOMAXPROCS(2) + success := make(chan bool, 1) + go func() { + buf := bytes.Repeat([]byte("\u035D"), 1024*1024) + buf = append(buf, "\u035B"...) + NFC.Append(nil, buf...) + success <- true + }() + timeout := time.After(1 * time.Second) + select { + case <-success: + // test completed before the timeout + case <-timeout: + t.Errorf(`unexpectedly long time to complete PerformanceTest`) + } +} diff --git a/cmd/discosrv/querysrv.go b/cmd/discosrv/querysrv.go index 48560ee01..32e9be19e 100644 --- a/cmd/discosrv/querysrv.go +++ b/cmd/discosrv/querysrv.go @@ -14,7 +14,7 @@ import ( "github.com/golang/groupcache/lru" "github.com/juju/ratelimit" - "github.com/syncthing/protocol" + "github.com/syncthing/syncthing/lib/protocol" ) type querysrv struct {